为什么只有NSLog会警告我使用%lu字符串格式说明符来表示NSUInteger?

20

由于某种原因,当我尝试进行以下操作时,会出现编译错误:

NSLog(@"row: %lu", indexPath.row);

其中row的类型为NSUInteger。我得到的错误是:

转换指定类型为“unsigned long”,但参数的类型为“NSUInteger”(又名“unsigned int”)

我可以执行以下操作,而不会出现编译错误:

NSString * string = [NSString stringWithFormat:@"row: %lu", indexPath.row];
我在两种情况下都使用了完全相同的格式字符串和替换参数,但是为什么NSLog会出问题而-stringWithFormat:却看起来完全正常?我的编译器是LLVM 1.6。

也许区别在于 NSLog() 是一个直接的 C 风格函数调用,而 -stringWithFormat: 则是一个 Objective-C 方法调用;因此编译器可能需要不同的机制来警告格式字符串和实际参数类型之间的不匹配。 - David Gelhar
Cocoa API 应该正在进行某种自动类型转换,这就是为什么我一直能够这样做的原因! - CIFilter
3个回答

35

iOS当前运行的所有设备都是32位的。如果你想消除警告:

NSLog(@"row: %lu", (unsigned long)indexPath.row);

[编辑:自 iPhone 5s 开始,iOS 不再总是 32 位。]


@Peter Hosey:我觉得我的困惑正是因为常见的API在Mac OS X中存在64位形式。无论如何,撇开愚蠢不谈,我仍然非常好奇为什么只有NSLog似乎会抱怨这个问题。 - CIFilter
3
这是因为你使用了-Wformat(正如应该做的那样)。有了它,当你传递一个类型与消耗它的格式说明符不匹配的参数时,Clang会发出警告。这是一种功能,特别是在可变参数中,其中没有类型安全性。在赋值和非可变参数中,隐式转换通常会做正确的事情,无需警告。 - Peter Hosey
1
@tc.: 在 Xcode 4.3.1 的 Clang 中,对于 NSLog 是有效的,但对于 stringWithFormat: 却无效,尽管两者都声明了相关属性。很奇怪。 - Peter Hosey
1
我添加了一个答案,提供了使用NS_BUILD_32_LIKE_64的替代方案,它的优点是不需要进行任何代码更改。 - Quinn Taylor
1
iOS 7和iPhone 5S(以及可能的下一代iPad)都是64位的,因此现在考虑这个问题的时候已经到了。 - Jacob Pritchett
显示剩余5条评论

7

我遇到了同样的问题,尽管@Wevah是正确的,他的答案完全有效,但还有另一种选择,不需要任何代码更改。请参阅以下苹果文档以获取详细信息:

String Programming Guide | Platform Dependencies

64-Bit Transition Guide for Cocoa | Building 32-Bit Like 64-Bit

NS_BUILD_32_LIKE_64 预处理器宏非常有用。您可以在Xcode项目设置中设置它(位于GCC_PREPROCESSOR_DEFINITIONS下),或者只需将#define NS_BUILD_32_LIKE_64 1放入预编译头文件(.pch)中。在我的应用程序中,这消除了11个警告,而无需进行任何代码更改。

这是因为在iOS上,unsigned intunsigned long的大小相同(4个字节),因此更改NSUInteger的typedef可以使编译器(和开发人员)满意,但硬件并不关心,因为在两种情况下都只进行整数运算。 :-)

0

苹果文档建议使用 %lu 和 %ld 将 64 位值转换为 32 位值。但如果您实际上使用了额外的 32 位,这会带来问题。格式字符串 %qu 和 %qd 指定了 64 位值(分别为无符号和有符号)。如果您想要编写能够在任何模式下编译的代码,则在参数列表中将声明为 NSUInteger 或 NSInteger 的值强制转换为 UInt64 或 SInt64,以避免警告。


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