9

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)?

cfischer
  • 24,452
  • 37
  • 131
  • 214

3 Answers3

16

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.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123
  • I consider this a flaw in the design of Objective-C that just makes things confusing and hard to debug. BUT, that is how it works, so is expected behavior. Just make your way around it somehow – tothemario Apr 29 '14 at 22:26
  • What you consider to be a flaw, others consider to be a great piece of design that simplifies code. – Carlos P Dec 04 '14 at 23:39
15

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.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
Trenskow
  • 3,783
  • 1
  • 29
  • 35
  • 7
    I would rather make that macro: "(x == y) || [x isEqual:y]", which covers all cases as well, and avoids the slow method call in the common special case. As an aside, you would still need the normal constructs to avoid double evaluation of x and y, it is a macro after all. – fishinear May 01 '13 at 11:17
  • @fishinear I did that for general isEqual, but for NSStrings, it complains ("Direct comparison of a string literal has undefined behavior"), so I did `#define IsStringEqual(left,right) ((!left && !right) || [left isEqualToString: right])`. – Ky - Dec 17 '15 at 16:50
2

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).

mipadi
  • 398,885
  • 90
  • 523
  • 479