Sending isEqual: to nil always returns NO - objective-c

If you send isEqual: to an object that happens to be nil, you always get NO back.
Is this the expected behavior? To be a feature instead of a bug, I would expect it to return YES if the other object is also nil, and NO otherwise? Semantically this seems the correct behavior.
In case my expectations are incorrect, what the recommended proceedure? Check for nil before sending isEqual: (and friends)?

Yes, this is the expected behavior. Any message to nil will return a result which is the equivalent to 0 for the type requested. Since the 0 for a boolean is NO, that is the result.

This is expected behaviour from Objective-C. This basically means that doing this
if ([nil isEqual:nil]) { ... }
evaluates to NO. Even though it doesn't make sense, when looking at it - and even though it's annoying - being able to send messages to nil is actually one of the really cool things about Objective-C. Saves you a lot of code sometimes.
My solution is to define this macro somewhere handy
#define IsEqual(x,y) ((x && [x isEqual:y]) || (!x && !y))
So when I need to test if two objects are equal:
if (IsEqual(obj1, obj2)) { ... }
or not equal:
if (!IsEqual(obj1, obj2)) { ... }
Hope this helps.

It is expected, for two reasons: (1) in Objective-C, sending a message to nil always returns a false-y value (nil, NO, 0, 0.0, etc.; or, more generally speaking, 0, which can be interpreted based on the expected return type of the method); (2) nil represents an unknown value, and two unknown values are not necessarily equal to each other.
If you want to see if an object is nil, use if (!obj) or if (obj == nil).

Related

What is the rationale behind Apple's pattern of checking return value rather than error?

Apple's guide on Using and Creating Error Objects gives the following code example:
NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:#"html" error:&theError];
if (success == NO) {
// Maybe try to determine cause of error and recover first.
NSAlert *theAlert = [NSAlert alertWithError:theError];
[theAlert runModal]; // Ignore return value.
}
and accompanies it with the statement:
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
I have always been wondering why is this pattern so important? Why should we ALWAYS check the return value? What's wrong if we check whether the error is nil or not?
This design is not terribly unusual, compare also errno in standard C.
The design has a number of potential advantages:
The function does not have to write through a pointer on success. This does not only make the implementation of such functions easier and less error prone, it can also be a small performance advantage (e.g. this prevent CPU caches from being invalidated if the function succeeds).
If we always check that the function failed before accessing an error, we can use that same error pointer for multiple functions. Otherwise, we might get a previous failure rather than the failure of the most recent function.
This makes validation code easier to write. E.g. a function could set the error by default. If all validations pass, the function can simply return success instead of having to reset the error variable.
A function can use the same error pointer when calling other functions, but a failure of these helpers doesn't necessarily imply a failure of the top function.
In your specific case, the variable NSError *theError; has not been initialized. Accessing that variable without assigning to it first would invoke undefined behaviour. The documentation only guarantees that the variable will be set in case of an error.
Imagine you implement a method in terms of a few other methods:
-(BOOL)sendCachedRequestReturningError: (NSError**)err {
BOOL success = [self readCachedRequestReturningError:err];
if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
success = [self sendUncachedRequestReturningError:err];
}
return success;
}
Now there are 4 code paths here:
There is a cached request. We'll just return success == YES and all is good.
A non-recoverable error occurs trying to read from the cache. readCachedRequestReturningError: will set err and set success == NO and the caller will call presentError: or whatever
An error occurs trying to do the network request. Same as #2, err is set, and success == NO.
There is no cache, but we can make a network request. readCachedRequestReturningError: will set err to a valid NSError{MYFileDomain, MYFileNotFoundCode}, but then sendUncachedRequestReturningError: will succeed and set success == YES, and not touch err at all, leaving the previous error in it. If you now check err instead of checking the return value, you will think there was an error when all went well.
Note: The code above is grossly simplified, because we only care about errors. Of course in a real program, the methods would probably have another return parameter for the actual reply from the request, or would return the reply or nil instead of a success BOOL. It would also probably check whether err is NULL.

In Swift, can you pass nil as an input to a function?

I'm reading many articles about how you shouldn't check an object for nil. It's a objC paradigm and it's a bad design and w/ swift it's been eliminated. So my question is, per example below, can you pass thru "group" as nil value? does the nil-checking mechanism happen when the function is called, hence removing the need to implement if(group==nil){..} ?
func deleteMembershipForGroup(group:GroupData){
}
You need to use an optional:
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group {
// not nil
}
}
Yes! Thomas Kilian is right and it works for me! You will then be able to pass a nil parameter. You will also notice that using optional variable, it will also removed the warning saying the variable "group" will always be true.
func deleteMembershipForGroup(group:GroupData?){
if let groupReal = group { <--- Warning gone!
// not nil
}
}

Assert with string argument not working as expected

EDIT: The issue was with the assert as people pointed out below. Thanks for the help!
I have a enum set that i'm trying equate, but for some reason its not working.
Its declared like so:
typedef NS_ENUM(NSUInteger, ExUnitTypes) {
kuNilWorkUnit,
kuDistanceInMeters,
//end
kuUndefined
};
And i'm using it here:
+(NSString*) ExUnitDescription: (ExUnitTypes) exUnit
{
if (exUnit == kuNilWorkUnit)
{
assert("error with units");
}
///.... more stuff
}
Xcode isnt triggering my assert. EDIT: the assert is just for testing. i've used NSLog as well. The conditional isn't evaluating to true even though the value is clearly kuNilWorkUnit.
Does anyone have any suggestions or ideas of what i'm doing wrong?
You want to do this:
+(NSString*) ExUnitDescription: (ExUnitTypes) exUnit
{
assert(exUnit != kuNilWorkUnit);
///.... more stuff
}
This is because, assert only stops execution if the expression you pass to it is false. Since a string literal is always non-zero, it will never stop execution.
Now, since you are using Objective C and it also looks like you want to have a message associated with your assert, NSAssert would be preferable.
+(NSString*) ExUnitDescription: (ExUnitTypes) exUnit
{
NSAssert(exUnit != kuNilWorkUnit, #"error with units");
///.... more stuff
}

Decoding with NSCoding: Does Value Exist For Key?

When using NSCoding and decoding values, is there a way to tell if a value exists for a given key? In other words, what I'm trying to do is...
if([decoder valueExistsForKey:#"myKey"]) //valueExistsForKey is not a real method :(
{
NSInteger *myInt = [decoder decodeValueForKey:#"myKey"];
}
else
{
//handle special case
}
The issue is that I have old versions of documents in my app that don't have the "myKey" value, and if they don't have it, using 0 for myInt (what happens if you decode a nonexistent key) is not the behavior I want. However, I can't just decode and check if myInt == 0, because it might legitimately be equal to 0.
Since the valueExistsForKey method does not seem to exist, how can I replicate this behavior?
How about containsValueForKey?

ObjectiveC: if (obj) {...} AND if (obj != nil) {...}, which is better?

I've seen a lot of ObjC code which do:
obj = [[SomeObject alloc] init];
if (obj) {
/// ...
}
but as I understood it, the value inside () is a boolean, and 0 indicates FALSE 1 indicates TRUE(there is another case in other language that 0 is true and 1 is false), and if a pointer does not point to anything, it is set to NULL(nil), which is #defined to be 0, so I wonder is it better if I do:
if (obj != nil) {
/// ...
}
as it IS checking if the obj is nil or not, no matter what value nil is, so it does not rely on that nil (or NULL) happen to be defined as 0?
edit:
after testing a bit, I have determined that modern compilers will actually create the same machine code for both cases;
orig post:
It is (negligibly, perhaps) more efficient to use
if(obj) {
since you do not need to create the intermediary boolean value (by evaluating the comparison expression). I'm not sure which "other language" you are referring to regarding the non-zero being FALSE; the closest thing I can think of is c programs returning 0 for "success" and anything else for "error". Every modern language I have ever worked with uses 0 as FALSE and any non zero value for TRUE.
In Objective-C, nil is literally 0 (treated like a pointer). It is not just a pointer to zero, it is zero as a pointer. It is therefore reliably equivalent to FALSE (or, in our nomenclature "NO").
edit
after testing a bit, I have determined that modern compilers will actually create the same machine code for both cases; probably because nil is essentiall typedef'd to 0, so it knows the two styles of checking are both saying "if this pointer is non-zero".
0 indicates FALSE 1 indicates TRUE
Close. In C (and Objective-C), a 0 evaluates to false, and a non-zero evaluates to true. So a nil (or NULL) pointer is "false", but any non-nil pointer is "true".
Your examples are essentially equivalent; neither is "better" than the other (unless you or your codebase has a style preference).