编译 arm64 和 32 位架构时的基础类型

8

当我为我的iOS应用程序的代码编译arm64时,我遇到了一个有趣的问题,涉及自定义Foundation类型的不同基本类型。比如说,我想要printf(或stringWithFormat)一个声明为NSUInteger的数字。

[NSString stringWithFormat:@"%u", _depth,

这将在编译arm64时产生警告,因为在arm64中,NSUInteger被声明为unsigned long。因此,我应该用"%lu"替换"%u",但是当编译32位架构(armv7)时,这种方法变得无效,因为在32位架构中,NSUInteger被声明为unsigned int。 我知道警告说"不应使用NSUInteger作为格式参数",那么我们继续看浮点数:

typedef CGFLOAT_TYPE CGFloat;

在64位系统上,CGFLOAT_TYPEdouble,而在32位系统上则是 float。因此,进行以下操作:
- (void)foo:(CGFloat)value;

然后

[self foo:10.0f]; 
[self foo:10.0]; 

在编译两个体系结构时,仍会产生警告。在32位架构上,第二次调用不正确(从double转换为float),在64位架构上,第一个将float转换为double(这是可以的,但仍然不好)。

很想听听您对这个问题的想法。


你真的会收到[self foo:10.0f]的警告吗?即使使用-Weverything,我也无法复现。 - Martin R
如果你阅读了苹果的64位转换指南,他们建议为printf风格的格式字符串进行强制类型转换。 - Mike Weller
1个回答

11

我曾经看到过一种(诚然很差的)做法,它利用了#define和编译时字符串字面值连接的魔力。像这样:

One (诚然很糟糕的) approach I've seen used is to use the magic of #define and compile time string literal concatenation. Like this:

// In your prefix header or something
#if __LP64__
#define NSI "ld"
#define NSU "lu"
#else
#define NSI "d"
#define NSU "u"
#endif

// Then later you can use these like this...
NSString* foo = [NSString stringWithFormat:@"%"NSU" things in the array.", array.count]);

很糟糕,但它能够工作。

另一种看似更常见的方法是在每个平台上将值向较大的类型转换,像这样:

NSString* foo = [NSString stringWithFormat:@"%lu things in the array.", (unsigned long)array.count]);

最近(即自从新的拳击简写语法出现以来),我发现自己变得懒惰,开始将所有东西都放在括号里,就像这样:

NSString* foo = [NSString stringWithFormat:@"%@ things in the array.", @(array.count)]);

可能还有更好的方法,但这些是我见过的最常见的。


4
需要说明的是,进行向上转换只是出于形式上的需要(以让编译器满意),但实际上并没有改变任何东西,因为在32位和64位架构中,sizeof(unsigned long)sizeof(NSUInteger)的大小是相同的。 - Martin R
自动完成建议完全向上转换(即%lu和(unsigned long)value) - Serhii
我感到困惑。你如何在编译时定义 NSI/NSU,然后在运行时神奇地选择适合每个架构的正确选项? - Rivera
选择并非在运行时发生,所有的选择都在编译时发生。NSIntegerNSUInteger也是在编译时定义的,这只是在模拟/类比那种行为。 - ipmcc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接