Try running this:
UIView *testView = nil;
NSLog(@"Take 1");
NSString *message = @"view doesn't exist";
if (!testView && !testView.subviews) {
message = @"this message should never appear" ;
}
NSLog(message);
NSLog(@"Take 2");
message = @"view doesn't exist";
if (testView != nil && testView.subviews != nil) {
message = @"this message should never appear" ;
}
NSLog(message);
NSLog(@"Take 3");
message = @"view doesn't exist";
if (!testView) {
if (!testView.subviews) {
message = @"this message should never appear" ;
}
}
NSLog(message);
NSLog(@"Take 4");
message = @"view doesn't exist";
if (testView != nil) {
if (testView.subviews != nil) {
message = @"this message should never appear" ;
}
}
NSLog(message);
Output I get is:
Take 1
this message should never appear
Take 2
view doesn't exist
Take 3
this message should never appear
Take 4
view doesn't exist
Why doesn't Obj-C short circuit for !testView
(in Take 1)?
Why does it go into !testView
when testView is clearly nil in Take 3?
Should I not be testing the function of a nil object (e.g. when I test for subviews
)?
The output you see is correct and the short-circuit behavior is working correctly too.
When boolean expressions are evaluated the result is considered to be true if the expression is not 0 or false if it is 0. So everywhere where you have if (something)
you can read this as if (something != 0)
. The !
operator is the negation, so if you expand it you get the following for your first case: !(testView != 0) && !(testView.subviews != 0)
. The double negation can be removed and you get (testView == 0) && (testView.subviews == 0)
which obviously is true (nil
is 0 too).
There the short-circuiting is also correctly applied, you just can't see it. To prove that you could use a little wrapper function for your tests:
id testFunc( id value ) {
NSLog(@"testFunc: %@", value );
return value;
}
And use that in your tests: if (!testFunc(testView) && !testFunc(testView.subviews))
To make it short, your assumptions about the boolean not operator !
are wrong. It goes into if (!testView)
because testView is nil
.