32

I have a database model class that is a NSObject. I have a set of these objects in a NSMutableArray. I use indexOfObject: to find a match. Problem is the model object's memory address changes. So I am overriding the hash method to return the model's row ID. This however does not fix it. I also have to override the isEqual: method to compare the value of the hash method.

What does the isEqual: method use to determine equality by default?

I'm assuming it uses the memory address. After reading the isEqual: documentation I thought it used the value from the hash method. Obviously, that is not the case as my attempt to override that value did not solve my initial problem.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Brenden
  • 7,708
  • 11
  • 61
  • 75
  • There are a few speculative answers here. Can anyone provide actual documentation that the default isEquals uses address> I'm not suggesting it doesn't, just would like to hear it from the horse's mouth. – DougW Aug 25 '11 at 21:02
  • @DougW I shared your feelings on this, so I tracked down an official reference and edited it into the accepted answer. – Mark Amery Aug 27 '13 at 20:55
  • You're reading the docs for the NSObject protocol, not the NSObject class. The protocol defines what the methods should do, the class fails to define what it actually does. It's always safest to override equals and hash to work how you want them to work. – Terry Wilcox Aug 27 '13 at 21:06
  • @TerryWilcox No, it's documented, as is referenced in the accepted answer. It's just not documented in the class reference, where you would expect it. It's absolutely not necessary to override `isEqual:` to perform explicit pointer comparision with `==` just to safely pass `indexOfObject:` an instance of some arbitrary `NSObject` subclass - you can trust that `NSObject`'s `isEqual:` will do pointer comparison. – Mark Amery Aug 27 '13 at 21:12
  • @MarkAmery I meant not documented in the class docs, which is where it should be documented. A one line mention in a different document is documentation by accident. And while you can trust that NSObject will do pointer equality, trusting that pointer equality is always sufficient is somewhat risky. – Terry Wilcox Aug 27 '13 at 21:23
  • 1
    @TerryWilcox I agree the documentation on this point is embarrassingly poor, but I think suggesting that you should always override `isEqual:` and not trust `NSObject`'s implementation is going too far. Obviously, though, if pointer equality (which is what `NSObject`'s `isEqual:` implementation checks for) isn't actually what you *want*, then you should override it. – Mark Amery Aug 27 '13 at 21:25
  • @MarkAmery I didn't say you can't trust NSObject's equals method, I said it's safest to always override it to do what you want. Like making equals compare the primary keys in a database object, rather than hoping your persistence layer caches your objects so well that it never thinks it needs to reload data. You can cross your fingers or you can be explicit. One is safer than the other. – Terry Wilcox Aug 27 '13 at 21:37

3 Answers3

31

As you've correctly guessed, NSObject's default isEqual: behaviour is comparing the memory address of the object. Strangely, this is not presently documented in the NSObject Class Reference, but it is documented in the Introspection documentation, which states:

The default NSObject implementation of isEqual: simply checks for pointer equality.

Of course, as you are doubtless aware, subclasses of NSObject can override isEqual: to behave differently. For example, NSString's isEqual: method, when passed another NSString, will first check the address and then check for an exact literal match between the strings.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Kenny Winker
  • 11,919
  • 7
  • 56
  • 78
  • A reference for this would be nice. – Mark Amery Aug 27 '13 at 16:57
  • 1
    ... so I added one. Strangely, the `NSObject` class reference doesn't even have an entry for `isEqual:`, let alone describe its behaviour anywhere, which seems like a glaring omission. I was able to track down an official reference in some obscure page in the 'conceptual' section of the docs, though. – Mark Amery Aug 27 '13 at 20:52
  • The Apple documentation is rife with cases where protocol methods are not documented in the class documentation. (Not to mention superclass methods.) You often have to look under a lot of rocks to find documentation for a given method. – Hot Licks Aug 27 '13 at 21:20
  • (I do wish the Apple documentation was more like Java's, where all of the inherited methods are at least listed with a class's description.) – Hot Licks Aug 28 '13 at 15:13
12

The answer about default implementation of isEqual: is comprehensive one. So I just add my note about default implementation of hash. Here it is:

-(unsigned)hash {return (unsigned)self;}

I.e it's just the same pointer value which is used in isEqual:. Here's how you can check this out:

NSObject *obj = [[NSObject alloc] init];
NSLog(@"obj: %@",obj);
NSLog(@"hash: %x",obj.hash);

The result will be something like this:

obj: <NSObject: 0x16d44010>
hash: 16d44010

Best Regards.

BTW in iOS 8 hash became a property not a method, but it's there.

ilnar_al
  • 932
  • 9
  • 13
5

I would assume that NSObject isEquals uses the == operator, and hash uses the memory address.

isEquals method should never uses hash as an absolute test for equality. It is guaranteed to have two objects having similar hashCode, if you search for enough objects (just create more than 2^32 different objects, and at least two of them will have the same hash).

In other words, hash requires the following spec: If two objects are equals, then their hash needs to be equal; however, if two objects' hash values are equals, they are not necessarily equal.

As a tip, you always should override isEquals and hashCode together.

notnoop
  • 58,763
  • 21
  • 123
  • 144