What is nil but not nil when inserted into NSMutableDictionary
I ran into some strange behavior on iPhone 6, iOS 8.3.
appVersion is the passed NSString * parameter.
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;
}
Release build failed:
A:(null):0
g
h
B:(null):0
and then fail ("object cannot be nil (key: appversion)").
Debug build returns:
a
b
c
d
e
g
h
What is zero but not zero?
source to share
I'm working on some legacy code and haven't noticed that there is a difference in method signature between .h and .m files.
The .h file has:
- (void) verifyWinner:(NSString*)baseAcctId
appVersion:(NSString*)appVersion
onComplete:(OnCompleteWinnerVerifier)onComplete __attribute__((nonnull));
I am assuming the original developer wanted onComplete to be set to zero. However, for whatever reason, __attribute__((nonnull))
binds to each of the parameters.
Because of the tag, __attribute__
Xcode will optimize all! = Nil checks for the release build, causing it to fail.
This issue only appeared with XCode 6.3. Thus, it is possible that Apple recently added optimization, otherwise there was a bug in 6.3 that binds __attribute__
with each of the parameters, and not just with the parameters that it is next to (for optimization purposes anyway).
source to share
The result looks strange. There is a good article; google for "What Every Programmer Should Know About Undefined Behavior" by Chris Lattner (the lead developer of Swift, so he should know what he's talking about).
It looks like after the very first NSLog instruction, the optimizing compiler decided that appVersion cannot be nil, because passing nil to NSLog would be undefined. This explains why ae are not printed.
"h" is printed because appVersion is a 64-bit pointer, int is only 32-bit, so a non-nil conversion of appVersion to int could just result in zero. The optimizer cannot remove this check, even if it is sure that appVersion is not zero.
And since the compiler is sure that appVersion is not zero, the last test fails, appVersion is stored in param, and because it is zero, you fail.
source to share