什么是 nil,但插入 NSMutableDictionary 时不是 nil
What is nil, but not nil when inserting into NSMutableDictionary
我 运行 在 iPhone 6、iOS 8.3 上遇到了一些奇怪的行为。
appVersion 是传入的 NSString* 参数。
NSLog(@"A:%@:%d",appVersion,(int)appVersion.length);
if (!appVersion)
NSLog(@"a");
if (appVersion == 0)
NSLog(@"b");
if (appVersion == nil)
NSLog(@"c");
if (appVersion == NULL)
NSLog(@"d");
if (appVersion == Nil)
NSLog(@"e");
if ([appVersion isEqual:[NSNull null]])
NSLog(@"f");
NSString* av = [NSString stringWithFormat:@"%@",appVersion];
if ([av isEqualToString:@"(null)"])
NSLog(@"g");
if (((int)appVersion) == 0)
NSLog(@"h");
if (appVersion) {
NSLog(@"B:%@:%d",appVersion,(int)appVersion);
params[@"appversion"] = appVersion;
}
应用程序的发布版本returns:
A:(null):0
g
h
B:(null):0
然后崩溃 ('object cannot be nil (key: appversion)')。
调试版本 returns:
a
b
c
d
e
g
h
什么是nil,又不是nil?
检查 [NSNull null]
NSNull class 定义了一个单例对象,用于在禁止将 nil 作为值的情况下(通常在数组或字典等集合对象中)表示空值。
结果看起来很奇怪。有一篇很好的文章; google 为 "What every programmer should know about undefined behaviour" 作者 Chris Lattner(Swift 的首席开发人员,所以他应该知道他在说什么)。
看起来在第一个 NSLog 语句之后,优化编译器决定 appVersion 不可能为 nil,因为将 nil 传递给 NSLog 将是未定义的行为。这解释了为什么不打印 a 到 e。
"h" 被打印,因为 appVersion 是一个 64 位指针,int 只有 32 位,所以将非 nil appVersion 转换为 int 只是 可能 有一个结果归零。即使确定 appVersion 不为零,优化器也无法删除该检查。
并且因为编译器确定 appVersion 不为 nil,所以最后的测试没有完成,appVersion 被存储到 param 中,因为它为 nil,所以你崩溃了。
我正在处理一些遗留代码,但没有注意到 .h 和 .m 文件的方法签名存在差异。
.h 文件有:
- (void) verifyWinner:(NSString*)baseAcctId
appVersion:(NSString*)appVersion
onComplete:(OnCompleteWinnerVerifier)onComplete __attribute__((nonnull));
我猜原开发者是想防止onComplete被设置为nil。但是,由于某些原因 __attribute__((nonnull))
与每个参数相关联。
由于 __attribute__
标记,XCode 正在优化发布版本的所有 != nil 检查,从而导致崩溃。
这个问题现在才出现在 XCode 6.3 中。因此,也许 Apple 最近添加了优化,或者在 6.3 中引入了一个错误,该错误将 __attribute__
与每个参数相关联,而不仅仅是它旁边的参数(无论如何出于优化目的)。
我 运行 在 iPhone 6、iOS 8.3 上遇到了一些奇怪的行为。
appVersion 是传入的 NSString* 参数。
NSLog(@"A:%@:%d",appVersion,(int)appVersion.length);
if (!appVersion)
NSLog(@"a");
if (appVersion == 0)
NSLog(@"b");
if (appVersion == nil)
NSLog(@"c");
if (appVersion == NULL)
NSLog(@"d");
if (appVersion == Nil)
NSLog(@"e");
if ([appVersion isEqual:[NSNull null]])
NSLog(@"f");
NSString* av = [NSString stringWithFormat:@"%@",appVersion];
if ([av isEqualToString:@"(null)"])
NSLog(@"g");
if (((int)appVersion) == 0)
NSLog(@"h");
if (appVersion) {
NSLog(@"B:%@:%d",appVersion,(int)appVersion);
params[@"appversion"] = appVersion;
}
应用程序的发布版本returns:
A:(null):0
g
h
B:(null):0
然后崩溃 ('object cannot be nil (key: appversion)')。
调试版本 returns:
a
b
c
d
e
g
h
什么是nil,又不是nil?
检查 [NSNull null]
NSNull class 定义了一个单例对象,用于在禁止将 nil 作为值的情况下(通常在数组或字典等集合对象中)表示空值。
结果看起来很奇怪。有一篇很好的文章; google 为 "What every programmer should know about undefined behaviour" 作者 Chris Lattner(Swift 的首席开发人员,所以他应该知道他在说什么)。
看起来在第一个 NSLog 语句之后,优化编译器决定 appVersion 不可能为 nil,因为将 nil 传递给 NSLog 将是未定义的行为。这解释了为什么不打印 a 到 e。
"h" 被打印,因为 appVersion 是一个 64 位指针,int 只有 32 位,所以将非 nil appVersion 转换为 int 只是 可能 有一个结果归零。即使确定 appVersion 不为零,优化器也无法删除该检查。
并且因为编译器确定 appVersion 不为 nil,所以最后的测试没有完成,appVersion 被存储到 param 中,因为它为 nil,所以你崩溃了。
我正在处理一些遗留代码,但没有注意到 .h 和 .m 文件的方法签名存在差异。
.h 文件有:
- (void) verifyWinner:(NSString*)baseAcctId
appVersion:(NSString*)appVersion
onComplete:(OnCompleteWinnerVerifier)onComplete __attribute__((nonnull));
我猜原开发者是想防止onComplete被设置为nil。但是,由于某些原因 __attribute__((nonnull))
与每个参数相关联。
由于 __attribute__
标记,XCode 正在优化发布版本的所有 != nil 检查,从而导致崩溃。
这个问题现在才出现在 XCode 6.3 中。因此,也许 Apple 最近添加了优化,或者在 6.3 中引入了一个错误,该错误将 __attribute__
与每个参数相关联,而不仅仅是它旁边的参数(无论如何出于优化目的)。