What is the NSObject isEqual: and hash default function? - objective-c

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.

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.

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.

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.

Related

Why does this Objective-C message make a hash?

I'm reading the open source of a library and I notice this method—hash.
-(NSUInteger)hash:(NSObject*)targetObject
{
return (NSUInteger)targetObject;
}
Why would this be the hash?
The target is a pointer to an object. That means it's an integer and you can definitely convert it to a NSUInteger.
It basically means that two distinct instances of a class will always have a different hash. That's probably a valid solution if isEqual is defined in terms of reference equality.
The default implementation of [NSObject hash] actually returns self.

Key-Value-Coding with arbitrary methods, not only properties

It seems that -valueForKey: and -valueForKeyPath: work with arbitrary methods, not only with properties. This seems very convenient:
I first stumbled upon it in Interface Builder, and then made some experiments:
// Thing.h
#import <Foundation/Foundation.h>
#interface Thing : NSObject
- (BOOL) alwaysYES;
- (BOOL) alwaysNO;
#end
// Thing.m
#import "Thing.h"
#implementation Thing
- (BOOL) alwaysYES
{
return YES;
}
- (BOOL) alwaysNO
{
return NO;
}
#end
I can call these methods via -valueForKey: and -valueForKeyPath: despite the fact that they are normal methods and no properties:
Thing *aThing = [[Thing alloc] init];
id result;
result = [aThing valueForKey:#"alwaysYES"];
NSLog(#"result is: %#", result);
result = [aThing valueForKeyPath:#"alwaysNO"];
NSLog(#"result is: %#", result);
Compiles, runs and gives the correct results. Is this documented anywhere? Can I safely use it? How can i understand it?
Cocoa's key-value coding (KVC) system is older than support for explicit properties (declared with #property) in Objective-C, so KVC is defined in terms of methods, not properties.
“Default Search Pattern for valueForKey:” in the Key-Value Coding Programming Guide spells out how valueForKey: decides what to do. It starts by looking for (amongst other things) a method whose name is exactly the key you passed to valueForKey:. Here is the full search pattern, quoted from the documentation:
Searches the class of the receiver for an accessor method whose name matches the pattern get<Key>, <key>, or is<Key>, in that order. If such a method is found it is invoked. If the type of the method's result is an object pointer type the result is simply returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types).
Otherwise (no simple accessor method is found), searches the class of the receiver for methods whose names match the patterns countOf<Key> and objectIn<Key>AtIndex: (corresponding to the primitive methods defined by the NSArray class) and <key>AtIndexes: (corresponding to the NSArray method objectsAtIndexes:).
If the countOf<Key> method and at least one of the other two possible methods are found, a collection proxy object that responds to all NSArray methods is returned. Each NSArray message sent to the collection proxy object will result in some combination of countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes: messages being sent to the original receiver of valueForKey:. If the class of the receiver also implements an optional method whose name matches the pattern get<Key>:range: that method will be used when appropriate for best performance.
Otherwise (no simple accessor method or set of array access methods is found), searches the class of the receiver for a threesome of methods whose names match the patterns countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: (corresponding to the primitive methods defined by the NSSet class).
If all three methods are found, a collection proxy object that responds to all NSSet methods is returned. Each NSSet message sent to the collection proxy object will result in some combination of countOf<Key>, enumeratorOf<Key>, and memberOf<Key>: messages being sent to the original receiver of valueForKey:.
Otherwise (no simple accessor method or set of collection access methods is found), if the receiver's class method accessInstanceVariablesDirectly returns YES, the class of the receiver is searched for an instance variable whose name matches the pattern _<key>, _is<Key>, <key>, or is<Key>, in that order. If such an instance variable is found, the value of the instance variable in the receiver is returned. If the type of the result is one of the scalar types supported by NSNumber conversion is done and an NSNumber is returned. Otherwise, conversion is done and an NSValue is returned. Results of arbitrary types are converted to NSValue objects, not just NSPoint, NSRange, NSRect, and NSSize types.
If none of the above situations occurs, returns a result the default implementation invokes valueForUndefinedKey:.
This is parallel to the fact that you can call these methods using property syntax:
BOOL ok = aThing.alwaysYES
In that case and in your case, exactly the same thing happens: the first thing the runtime tries is to treat this as a getter method. What you've written is a getter method.
As for your question "can I safely use it", safely yes, but what you're doing is kind of silly, since you know (and have declared) that these methods exist. KVC is about probing to see whether methods exist. If you have a reason to specify one of these methods by string name, there are better ways to call it than using KVC.
Properties are nothing special at runtime; they generate a getter and setter (if not readonly) which conforms to KVC; for example:
#property NSString *aString;
will generate:
- (NSString)aString {
...
}
- (void)setAString(NSString *string) {
...
}
just as if you had declared those methods in the header (which itself is optional).
See the Key Value Coding Fundamentals documentation.

What does Objective-C property get resolved to in runtime?

What does Objective-C property get resolved to in runtime? Will calling [obj valueForKey:#"property"] always yield the same result?
e.g.
obj.property
First, note that obj.property is precisely the same as [obj property]. Dot syntax is just syntactic sugar. While there are some small run-time implementation details related to properties that are different than other methods, for the purposes of this discussion, think only in terms of "I have an ivar named _key and a method called -key." The fact that you created that ivar and method by declaring a property is irrelevant for valueForKey:.
valueForKey: is a method, and it can be overridden by a class to return whatever it likes. The default behavior is that valueForKey: will first look for a method whose name matches the key, and will return the result of that. In the vast majority of cases, this means that if you have a property, then valueForKey:#"property" will return the value of it.
The full search path for the default implementation of valueForKey: is explained in "Accessor Search Implementation Details", but here is the "short" version:
get<Key>, <key>, is<Key> (yes, the first place it looks is getKey, which is a little embarrassing because you should not prefix getters with get unless they return values by reference, but there you go; it is the first thing checked.)
countOf<Key>, objectIn<Key>AtIndex:, and <key>AtIndexes. If a legal combination of these are found, then an NSArray-like proxy object is returned.
countOf<Key>, enumeratorOf<Key>, and memberOf<Key>:. If all three are found, then an NSSet-like proxy object is returned.
If accessInstanceVariablesDirectly is YES (the default), then ivars are checked, named _<key>, _is<Key>, <key>, or is<Key>. Yes, this is a way to access an object's private ivars.
If everything else failed, then it calls valueForUndefinedKey:, which is free to return a result (and this is in fact a very useful thing to do if you want a generic key/value store).
But nine times out of ten, you're going to get the value of the method named <key>.
Side note: valueForKey: returns an object. If the return is a number-like scalar (including BOOL), it will return an NSNumber. Otherwise it will return an NSValue. There is some special handling for NSPoint, NSRange, NSRect, and NSSize (on Mac; on iOS, only NSRange is handled specially I believe).
obj.property is the same as [obj property], not [obj valueForKey:#"property"];. The latter is part of a system called Key Value Coding that's separate from properties.

Does Apple Provide default implementation of isEqual:

In C there is a default implementation of equality operator. Go through all the member and verify that they satisfy the equality operator. The default is somewhat stupid because if an object contains pointer then the equality operator of the member would be performed on the pointer.
Still, it's good enough for my purpose.
So does it?
Or are we expected to implement isEqual and the corresponding hash for everytime we create a custom object that may we want to use isequal for.
It seems to me the "default" implementation is to simply compare the pointer of the object and not it's member. Am I correct here? It's even worse than C++ standard comparison. That's what I want to verify.
It seems to me if our class is the immediate children of NSObject then isEqual will simply call it's parent's isEqual and that simply compare pointers.
Am I correct here? I am just wanting to make sure of that.
I think that NSObject’s implementation does pointer comparison, and various other classes from the SDK do what’s most appropriate, ie. NSString does comparison on string contents, NSArray compares content equality, and so on. If you want to have “better” equality defined for your custom objects, you have to decide about the semantics and implement it yourself.
Its a little confusing because of the way Apple separates their docs between protocols and interfaces.
#protocol NSObject
- (BOOL)isEqual:(id)object;
This is a required method to be implemented so NSObject (the class) definitely implements this although you wouldnt know it from looking at the class definition on apples dev site. This is directly from the headers in xcode.
In general without implementing a custom isEqual you will expect to only get pointer identity and thats ok in many cases. Systems need to be designed around the way you identify unique instances regardless of the peculiarity of a particular feature such as hash and isEqual. If you need to test for object equality beyond the pointer then you just have to do that.
As NSObject provides isEqual:, and all your objects are descendants of NSObject, then the the simple answer is that a default implementation is provided.
Now you are concerned over the algorithm this default uses, and in a comment write "I wouldn't be sure simply by testing". Let's look at testing, just for fun ;-)
Now isEqual: is a rather fundamental method, if Apple decided to change its semantics the consequences could be significant and not good. So while Apple is free to change how it is implemented provided the semantics remain the same, which means the same objects compare equal after the change as before. Now you've mentioned three possible algorithms isEqual: could use:
Pointer comparison - is it the exact same object
Shallow comparison - do the fields of the object have the same value compared directly
Deep comparison - do the non-pointer-valued fields compared directly have the same value, and do the pointer-valued fields compare equal using isEqual:
These all have different semantics, whichever one Apple has chosen it can't change without breaking a lot of code. And different semantics means you can test...
Coding as I type, errors expected! Only important bits included:
#implementation A
- (BOOL) isEqual:(id)other
{
NSLog(#"A.isEqual called");
return self == other; // true iff same object
}
#end
#interface B
#property (readwrite) int anInteger;
#property (readwrite) A *anA;
#end
#implementation B
#synthesize anInteger, anA;
#end
// Let's test the algorithm
A *myA = [A new];
B *bOne = [B new];
B *bTwo = [B new];
bOne.anInteger = 42;
bOne.anA = myA;
bTwo.anInteger = 42;
bTwo.anA = myA;
// What output is produced (all of it!)
NSLog(#"[bOne isEqual:bTwo] -> %#", [bOne isEqual:bTwo] ? #"Yes" : #"No");
HTH a little.

Techniques for implementing -hash on mutable Cocoa objects

The documentation for -hash says it must not change while a mutable object is stored in a collection, and similarly the documentation for -isEqual: says the -hash value must be the same for equal objects.
Given this, does anybody have any suggestions for the best way to implement -hash such that it meets both these conditions and yet is actually calculated intelligently (i.e. doesn't just return 0)? Does anybody know how the mutable versions of framework-provided classes do this?
The simplest thing to do is of course just forget the first condition (about it not changing) and just make sure I never accidentally mutate an object while it's in a collection, but I'm wondering if there's any solution that's more flexible.
EDIT: I'm wondering here whether it's possible to maintain the 2 contracts (where equal objects have equal hashes, and hashes don't change while the object is in a collection) when I'm mutating the internal state of the object. My inclination is to say "no", unless I do something stupid like always return 0 for the hash, but that's why I'm asking this question.
Interesting question, but I think what you want is logically impossible. Say you start with 2 objects, A and B. They're both different, and they start with different hash codes. You add both to some hash table. Now, you want to mutate A, but you can't change the hash code because it's already in the table. However, it's possible to change A in such a way that it .equals() B.
In this case, you have 2 choices, neither of which works:
Change the hashcode of A to equal B.hashcode, which violates the constraint of not changing hash codes while in a hash table.
Don't change the hashcode, in which case A.equals(B) but they don't have the same hashcodes.
It seems to me that there's no possible way to do this without using a constant as a hashcode.
My reading of the documentation is that a mutable object's value for hash can (and probably should) change when it is mutated, but should not change when the object hasn't been mutated. The portion of the documentation to which to refer, therefore, is saying, "Don't mutate objects that are stored in a collection, because that will cause their hash value to change."
To quote directly from the NSObject documentation for hash:
If a mutable object is added to a
collection that uses hash values to
determine the object’s position in the
collection, the value returned by the
hash method of the object must not
change while the object is in the
collection. Therefore, either the hash
method must not rely on any of the
object’s internal state information or
you must make sure the object’s
internal state information does not
change while the object is in the
collection.
(Emphasis mine.)
The question here isn't how to meet both of these requirements, but rather which one you should meet. In Apple's documentation, it is clearly stated that:
a mutable dictionary can be put in a hash table but you must not change it while it is in there.
This being said, it seems more important that you meet the equality requirement of hashes. The hash of an object should always be a way to check if an object is equal to another. If this is ever not the case, it is not a true hash function.
Just to finish up my answer, I'll give an example of a good hash implementation. Let's say you are writing the implementation of -hash on a collection that you have created. This collection stores an array of NSObjects as pointers. Since all NSObjects implement the hash function, you can use their hashes in calculating the collection's hash:
- (NSUInteger)hash {
NSUInteger theHash = 0;
for (NSObject * aPtr in self) { // fast enumeration
theHash ^= [aPtr hash];
}
return theHash;
}
This way, two collection objects containing the same pointers (in the same order) will have the same hash.
Since you are already overriding -isEqual: to do a value-based comparison, are you sure you really need to bother with -hash?
I can't guess what exactly you need this for of course, but if you want to do value-based comparison without deviating from the expected implementation of -isEqual: to only return YES when hashes are identical, a better approach might be to mimick NSString's -isEqualToString:, so to create your own -isEqualToFoo: method instead of using or overriding -isEqual:.
The answer to this question and the key to avoiding many cocoa-bugs is this:
Read the documentation carefully. Place every word and punctuation on a golden scale and weight it as it was the world's last grain of wheat.
Let's read the documentation again:
If a mutable object is added to a collection that uses hash values to determine the object’s position in the collection, [...]
(emphasis mine).
What the writer of the docs, in his/hers eternal wisdom, mean by this is that when you are implementing a collection, like a dictionary, you shouldn't use the hash for positioning since that can change. In other words it has little to do with implementing -hash on mutable Cocoa objects (which all of us thought it had, assuming the documentation has not changed in the last ~10 years since the question was asked).
That is why dictionaries always copy their keys - so they can guarantee
that the hash value won't change.
You will then ask the question: But, good sir, how does NSMapTable and similar handle this?
The answer to this is according to the documentation:
"Its keys or values may be copied on input or may use pointer identity for equality and hashing."
(emphasis mine again).
Since we were so easily fooled by the documentation last time, let's run a little experiment to see for ourselves how stuff actually work:
NSMutableString *string = [NSMutableString stringWithString:#"so lets mutate this"];
NSString *originalString = string.copy;
NSMapTable *mutableStrings = [NSMapTable strongToStrongObjectsMapTable];
[mutableStrings setObject:originalString forKey:string];
[string appendString:#" into a larger string"];
if ([mutableStrings objectForKey:string] == nil)
NSLog(#"not found!");
if ([mutableStrings objectForKey:originalString] == nil)
NSLog(#"Not even the original string is found?");
for (NSString *inCollection in mutableStrings)
{
NSLog(#"key '%#' : is '%#' (null)", inCollection, [mutableStrings objectForKey:inCollection]);
}
for (NSString *value in NSAllMapTableValues(mutableStrings))
{
NSLog(#"value exists: %#", value);
}
Surprise!
So, instead of using pointer equality, they focus on the words "may" here which in this case mean "may not", and simply copy the hash value when adding stuff to the collection.
(All this is actually good, since it would be quite difficult to implement NSHashMap, or -hash, otherwise).
In Java, most mutable classes simply don’t override Object.hashCode() so that the default implementation returns a value that is based on the address of the object and doesn’t change. It might just be the same with Objective C.