I read Mike Ash's Objective-C pitfalls page, and now I'm paranoid about implicitly casting variables of type id to BOOL.
Assume I have a 'dangerous' pointer with the lower bits zeroed out, so that casting it to a BOOL would produce NO even though it points to a valid object. Let's call this 'dangerous' pointer foo.
How do simple if statements work?
Does the if statement cast foo to a BOOL when it evaluates the condition? Or does it cast to a pure boolean?
if( foo )
{
[myArray addObject:foo];
}
Does it work the same way in ternary expressions?
// Does this break when foo is 'dangerous'?
self.titleLabel.hidden = foo ? YES : NO ;
Or will the ternary only break if I do this:
// I'm pretty sure it breaks now
self.titleLabel.hidden = ((BOOL)foo) ? YES : NO ;
I feel like I'm missing something basic about logical operations in C, and how they relate to BOOL. Please help enlighten me.
When does Objective-C implicitly cast to BOOL?
Never, since this phrase is semantically incorrect. A cast is, by definition, explicit. What you're asking about is called an "implicit type conversion" (coercion - thanks, Josh!).
Also, strictly speaking, the conditionals in Objective-C are not special: they're inherited from C. So expressions, when needed to evaluated as booleans, aren't treated as BOOL, but as int.
// Does this break when foo is 'dangerous'?`
self.titleLabel.hidden = foo ? YES : NO;
No, it doesn't. (expr) when used as a "boolean" expression, is equivalent to (expr != 0). No risks here.
// I'm pretty sure it breaks now
self.titleLabel.hidden = ((BOOL)foo) ? YES : NO;
Now this can break, since BOOL is just typedeffed to signed char, which is 8 bit on iOS. Thus, if foo, which is a (32-bit) pointer, gets truncated to 8 bits, and the lower 8 bits of the pointer were all zero, but the pointer itself wasn't nil, this will incorrectly report false. You don't want to do this superfluous, ugly and dangerous cast. If you want to be more explicit, write
var = (obj != nil) ? valueOne : valueTwo;
instead.
There's no casting in your first two samples. Writing if(expr) is equal to writing if(expr == 0).
Your third example might indeed break in some cases, as described in the article you refer to.
Related
+ (id)checkWithBlock:(BOOL (^)(id))block
{
return [[[OCMBlockConstraint alloc] initWithConstraintBlock:block] autorelease];
}
[OCMArg checkWithBlock:^(id value) { return YES && YES; }];
I am passing in a return YES && YES to a block that returns a BOOL, and yet am getting this error:
Incompatible block pointer types sending 'int (^)(__strong id)' to parameter of type 'BOOL (^)(__strong id)'
You're missing the BOOL declaration when calling the block method. If you use Xcode's autocomplete, it should create this for you:
[OCMArg checkWithBlock:^BOOL(id value) {
return YES && YES;
}];
There is an answer and there is a good comment. However, we have to put things together to understand that:
A. In C the result of a logical and operation is an int. From the standard (ISO/IEC 9899:TC3):
Semantics
The && operator shall yield 1 if both of its operands compare unequal to 0; otherwise, it yields 0. The result has type int.
B. Someone at Apple decided to have inferred types for blocks and stronger typing. This is a bad idea for C, because C does not support inferred types and is weakly typed. It solves the category of problems you have with implicit (or explicit) castings (weak typing), not with type inference and type strength. So the type strength of blocks does not fit for C.
C. Solution #1 (old): The original solution was, to explicitly cast the return value:
return (BOOL)(YES && YES);
D. Solution #2 (new): Later that many developers complained Apple for having un-C-ish strong typing combined with type inference for blocks that Apple introduced an explicit return type for blocks as in remus' answer. If you have such an explicit type, return expressions are implicitly casted to that type.
Z. The funny part of the story: Even Apple failed with that inferred types they try it again in Swift. And there it is much more complex and obfuscated if you have more than 1+2. One of the "cool feature you should use sparingly" stuff in Swift.
How do I override the truth-value of my class instance, or its ! operator?
For example (simplified names/usage):
MyClass variable = [MyClass alloc] initWithValue: nil];
...
if (!variable) { NSLog(#"'Not variable value' works"); }
I've searched for two possible solutions but couldn't find anything useful. In some languages i would overload an isTrue, asBool, etc function; or override the unary ! operator FOR THAT CLASS (not the main NOT operator.
My initial reaction to this problem was: You don't need to in Objective-C, you're going at it wrong. While true, I have lost over hours debugging some code that had the above (!variable) instead of
if (!variable.value) { NSLog(#"'Not variable value' works"); }
MyClass has property value among many others, which you can set or not. It defines whether or not you do something so it is common to need if (!variable.value){ NSLog(#"Warning, value not set"); }
So I want to overload the ! or isTrue function to check whether or not variable.value is set instead of merely checking if variable is linked to an address. This would make my code more readable and make my class more useable.
To be clear, in this example, variable points to an alloc-init'ed object, where variable.value = nil for example.
For example this can be done in python by overloading __ nonzero __.
As a side question that would answer this question: How does the truth value of an object work in Objective-C?
You can't override these things in ObjC. ObjC behaves just like C in this regard-- object references are pointers, either valid or nil-- a nil value evaluates to NO in a boolean expression and any non-nil value will appear as YES.
The canonical check for "is this thing an invalid pointer" is if (!thing) { ... }.
If you are always doing this:
if (!variable.value) ...
then perhaps variable is of type NSNumber or some object container for a primitive? It's hard to tell without context what pattern you're using and whether there's a better idiom for this.
An object in Objective-C has the same meaning in a boolean expression as a pointer in C, because it is a C pointer. If the pointer is NULL -- or nil for an object -- then it's false; otherwise it's true.
You can't override operators in Objective-C.
This said, !variable.value does indeed test whether value is nil (assuming that value has an object type) -- the dot operator resolves to a message send [variable value] whose return value is then negated.
I have seen many posts here on Stack Overflow warning Objective-C users not to compare objects with the == operator. For example, the following is always recommended against.
if (string1 == string2) {} // string1 and string2 are both of type NSString *
I understand this warning as the == operator in this case checks for pointer equality, not the actual values of string1 and string2. If this is the case, why do I so often see people comparing their objects to nil with the == operator. For example, I have seen the following several times.
if (obj == nil) {} // obj is of type id, or any other object
If comparing pointers with the == operator is bad practice on things like NSStrings (or NSNumbers or NSArrays etc), then why is it so commonplace with nil.
Is this the standard because there is only one type of nil i.e.: nil always equals nil? Why are direct comaprisons using the == operator frowned upon between objects, but let slip when comparing to nil?
Thank your for your time.
Using == tests for pointer equality, which seems to be what you want to test for in this particular case. You are checking whether some pointer is a nil pointer. Specifically, a pointer that equals zero. Comparing objects is more complicated as you've pointed out. Equality between objects often relies on some internal part of the two objects being "the same".
In the case of strings, "equality" can mean "has the same characters" but it could also mean "has the same characters (ignoring capitalization)". There are also cases where you might want a nil string to equal an empty string or even a string with just whitespace. The point is that you must understand the different methods of determining equality in order to implement your own definition of equality for your objects.
Note that checking for pointer equality is almost never what you want to use when comparing things like strings or arrays. There are some interesting optimizations that can throw you off. For example, all empty NSArray's point to the same singleton object.
nil is like NULL from C or std::nullptr in C++. Basically points to nothing.
When a object in Objective-C is unallocated (haven't been alloc), the pointer will be nil.
So if (obj == nil) will check if obj is allocated (or initialized as they often happen together)
This technique is often used when writing a custom initializer or checking if some object exists or not.
According to Mike Ash's blog post casting to BOOL can fail 6% of the time since a pointer is larger than a BOOL. Because of this he recommends that if you are checking if an object exists you should check against nil
if (self != nil) {
instead of what Apple templates do which is
if (self) {
Do Apple's templates contain a bug or is there some other syntactical sugar I don't know about?
since they cast to BOOL
No, they don't. There is no cast performed at all. There is, however, evaluation of an expression as a logic value. In the case of pointers (which nil and self are), a NULL pointer evaluates to false, anything else to true. This is not a bug, just a shorthand way and it's perfectly valid C.
Concerning why one might want to perform an explicit comparison against NULL or nil: in some cases, it may be more readable, especially if it's not obvious at first glance of what type and range the expression is. However, in Objective-C and Cocoa, it's such a common idiom in a constructor to do this, that any experienced programmer will grasp it without problem.
Objective-C is based on C, and in C the if statement does, maybe surprisingly, not operate on boolean values.
C has a hierarchy of types:
The char, integer, and enumeration (enum) types are the integer types
The float, double and long double are the real floating types
There are three complex number types which together with the real floating types are termed simply the floating point types
The integer types and floating point types combined form the arithmetic types
The arithmetic types along with the pointer types form the scalar types
Phew!
Now to the if statement, it is of one of the forms:
if ( expression ) statement
if ( expression ) statement else statement
Where expression must be of any scalar type. The first (or only) statement is executed if the expression compares unequal to 0, the second (if present) statement is executed if the expression compares equal to 0.
So:
if (self) ...
and
if (self != nil) ...
are identical in result.
The first expression self is some pointer type, which is a scalar type, and is compared for being unequal to the zero of the pointer type (represented in code by nil). The second expression self != nil is an equality-expression and yields either 0 or 1 of type int, which is also a scalar type...
So technically the second form involves the intermediate production of an int value, but no compiler will actually produce one (unless you assign the result of the expression to a variable of course).
Notes:
Somewhat bizarrely if ( sin( angle ) ) ... is valid...
The switch statement operates on integer types and not scalar types
HTH
No, this can't fail if the pointer isn't nil, it's safe and the only difference is the syntax, and I wouldn't recommend one or another way, since they're the same.
You aren't casting self to a BOOL, you' re just checking if self is different from zero.
Edited
As per Martin's comment I was logging wrong debug messages.
It's the same
Here is a simple test
NSString *str = nil;
// suppose our string points to a string object whose starting address is 0x4500
//I'm assigning it manually so that I can test
str = 0x4500;
NSLog(#"%d",str);
if (str) {
NSLog(#"It's non-nil");
}
else {
NSLog(#"It's nil");
}
if (str == nil) {
NSLog(#"It's nil");
}
else {
NSLog(#"It's non-nil");
}
Output is :
It's non-nil
It's non-nil
What syntax do you think is better/more readable?
if(!myViewController.view.superview)
or:
if(myViewController.view.superview == nil)
Thanks!!
The two are very close, it comes down to personal taste or the conding standards of the project in question.
Saying !myViewController.view.superview meaning "no superview" is very clear.
Saying myViewController.view.superview == nil meaning superview is nil is also very clear.
I'd probably favor the former since if I was writing in English, I'd say:
if there is no superview then
I wouldn't say
if the superview is nothing then
But they are so close, and entirely equivalent, that it is hardly worth even being consistent with. Don't get me wrong, I'm all for consistency in general, it is just there really is no difference in readability between the two.
I use the second form because the intention is more clear that way.
Here is a link to Google's Objective C coding standards:
http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml
They don't explicitly say which way they prefer but they do say to only use nil for logic checks, of which your example above would qualify.
Personally for a long time I used the latter expression, but reversed. Using "foo == nil" (or nil == foo, to avoid bugs caused by forgetting one '=') is more pedantic. Eventually you will get tired of typing it, and the first version is also immune to the accidental nil assignment bug.
It's good for new coders to be verbose in what they're coding, as it provides practise at forcing them to think about what's really going on, but later, of course switch to a version that is faster if it is equivalent.
If for some insane reason, nil pointers were not 0 anymore, but some other invalid value (there's entire gigantic regions of memory which are invalid as pointers), then using '!' wouldn't work anymore, but that will never happen (or if it did, they'd add support to the compiler to overload '!' so that it meant "not invalid" when used with an object pointer and would do the right thing anyway, or else the Objective-C developers of the world would go crazy).
The only subtle problem with this is that it can start to train you to confuse the value of C's boolean expressions with the values of other types, which they aren't the same thing. So, if you start to think a boolean expression is just a BOOL, say, you might assume that assigning any non-zero value to a BOOL variable will do what you want, but it won't. Since a BOOL is just a char (currently), if you do something like:
- (BOOL)checkFoo {
BOOL foo = [bar count]; // imagine count is > 255
if(foo)
[self doSomething];
return foo;
}
where implicitly casting 256 or higher to BOOL gets you zero (NO) by truncation, not YES, which is what you want, versus
- (BOOL)checkFoo {
BOOL foo = ([bar count] > 0);
if(foo)
[self doSomething];
return foo;
}
or
- (BOOL)checkFoo {
if([bar count]) {
[self doSomething];
return YES;
}
return NO;
}
All I'm saying is, make sure you understand the background and the subtleties.