ObjectiveC测试int是否为long / CGFloat是float还是double

Apple现在要求所有iOS代码都是32/64位二进制。 这很好 – 除了他们的一些库不完全支持二元性。

例如,一个项目使用NSScanner,它支持“scanInteger”(正确)但不支持“scanCGFloat” – 而是必须“scanFloat”或“scanDouble”(以方法名称编码!)。


更新:NSScanner是一个比我意识到的更糟糕的例子; 它需要指针作为args。

- (BOOL)scanFloat:(float *)result; - (BOOL)scanDouble:(double *)result; 

…啊。


它会影响一大堆东西 – 另一个例子是math.h(类似地:float vs double在函数名中编码),虽然你可以至少切换到tgmath.h并摆脱“type-is-in-这个名字“愚蠢。

什么是这个问题的正确,一般的解决方案?

C11引入了一个新function“通用选择”,可用于让编译器选择正确的方法,具体取决于CGFloat的类型。

写成NSScanner类别方法:

 @implementation NSScanner (MyCategory) -(BOOL) myScanCGFloat:(CGFloat *)cgFloatValue { return _Generic(*cgFloatValue, double: [self scanDouble:(double *)cgFloatValue], float: [self scanFloat:(float *)cgFloatValue]); } @end 

备注:

  • _Generic关键字在C11标准的“6.5.1.1通用选择”中描述( http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf )。 另一种描述如下: http : //www.robertgamble.net/2012/01/c11-generic-selections.html 。
  • 选择匹配代码由编译器完成,而不是在运行时完成。
  • 选择检查CGFloat是否兼容floatdouble ,而不是当前目标体系结构是32位还是64位。
  • 此解决方案不依赖于任何预处理器宏。
  • 从Xcode 5.0.2开始,即使在默认的GNU99模式下,Clang也支持_Generic关键字。 我还没有测试过早期的Xcode / Clang版本。

上一个答案:一个可能的解决方案是模仿CGFloat的定义并让预处理器选择正确的版本:

 CGFloat f; #if __LP64__ [scanner scanDouble:&f]; #else [scanner scanFloat:&f]; #endif 

或者您定义自定义宏:

 #if __LP64__ #define scanCGFloat scanDouble #else #define scanCGFloat scanFloat #endif // ... CFFloat f; [scanner scanCGFloat:&f]; 

或者,使用临时变量:

 double tmp; [scanner scanDouble:&tmp]; f = tmp; 

啊,在标题中路由,我刚发现这个(不是苹果官方“升级到64位”文档的mentioend !!!)

#define CGFLOAT_IS_DOUBLE 1

#define CGFLOAT_IS_DOUBLE 0

…所以我猜这是用的东西?

作为参考(我觉得这是一个糟糕的,狡猾的解决方案),到目前为止我最好的想法是:

注意 – 我正在使用NSScanner作为示例,但这是一个普遍的问题/解决方案!

 if( sizeof( var ) <= 4 ) [scanner scanFloat:&var]; else [scanner scanDouble:&var]; 

...但这不会修复编译器错误(在两个平台上),它只会使它“在运行时做正确的事情”作为黑客攻击。 肯定不是一个好的解决方案?

最简单的解决方案是扫描为double,然后将结果转换为CGFloat。

 -(CGFloat) scanCGFloat { double myDouble; [scanner scanDouble: &myDouble]; return (CGFloat) myDouble; } 

对NSInteger和NSUInteger使用类似的技巧,始终扫描为(无符号)长并转换为NS(U)整数。