當你要實現相等性的時候記住這個約定:你需要同時實現`isEqual` and the `hash`方法。如果兩個對象是被`isEqual`認為相等的,它們的 `hash` 方法需要返回一樣的值。但是如果 `hash` 返回一樣的值,并不能確保他們相等。
這個約定是因為當被存儲在集合(如 `NSDictionary` 和 `NSSet` 在底層使用 hash 表數據的數據結構)的時候,如何查找這些對象。
~~~
@implementation ZOCPerson
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[ZOCPerson class]]) {
return NO;
}
// check objects properties (name and birthday) for equality
...
return propertiesMatch;
}
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}
@end
~~~
一定要注意 hash 方法不能返回一個常量。這是一個典型的錯誤并且會導致嚴重的問題,因為使用了這個值作為 hash 表的 key,會導致 hash 表 100%的碰撞
你總是應該用 `isEqualTo<#class-name-without-prefix#>:` 這樣的格式實現一個相等性檢查方法。如果你這樣做,會優先調用這個方法來避免上面的類型檢查。
一個完整的 isEqual* 方法應該是這樣的:
~~~
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[ZOCPerson class]]) {
return NO;
}
return [self isEqualToPerson:(ZOCPerson *)object];
}
- (BOOL)isEqualToPerson:(Person *)person {
if (!person) {
return NO;
}
BOOL namesMatch = (!self.name && !person.name) ||
[self.name isEqualToString:person.name];
BOOL birthdaysMatch = (!self.birthday && !person.birthday) ||
[self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
~~~
一個對象實例的 `hash` 計算結果應該是確定的。當它被加入到一個容器對象(比如 `NSArray`, `NSSet`, 或者 `NSDictionary`)的時候這是很重要的,否則行為會無法預測(所有的容器對象使用對象的 hash 來查找或者實施特別的行為,如確定唯一性)這也就是說,應該用不可變的屬性來計算 hash 值,或者,最好保證對象是不可變的。