在實際的開發過程中,常常須要比較對象是否相同。可是,==
操做符比較出來的結果可能並非想要的,由於這個操做符比較的是指針自己,並非對象。因此大部分狀況下,比價對象應該使用NSObject
協議中的isEqual:
。c++
好比:數據庫
NSString *foo = @"Student 123";
NSString *bar = [NSString stringWithFormat:@"Student %i",123];
BOOL equalA = (foo == bar); //equalA = NO
BOOL equalB = [foo isEqaul:bar]; //equalA = NO
BOOL equalC = [foo isEqualToString:bar]; //equalC = YES
複製代碼
這裏能夠看出,NSString有着本身的等同性判斷方法isEqualToString:
。調用這個方法會比調用isEqual:
方法快,由於後者須要執行額外的步驟檢測受測對象的類型。數組
判斷對象等同性在NSObject協議中有兩個關鍵方法:bash
- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
複製代碼
這兩個方法的默認實現是兩個對象內存地址徹底(指針值)相等時,這兩個對象才相等。在自定義對象中,正確重寫這兩個方法的步驟應該是:性能
重寫isEqual:返回YES --> Hash值相等 --> 調用isEqual:方法判斷對象等同
複製代碼
@interface EOCPerson : NSObject
@property (nonatomic, copy)NSString *firstName;
@property (nonatomic, copy)NSString *lastName;
@property (nonatomic, assign)NSUInteger *age;
@end
複製代碼
很明顯,當這個對象全部字段都相同時,就能夠認定兩個對象相等了。因此重寫isEqual:
方法:優化
- (BOOL)isEqual:(id)object {
///判斷指針相等
if (self == object) return YES;
///不一樣類不相等
if ([self class] != [object class]) return NO;
EOCPerson *otherPerson = (EOCPerson*)object;
if (![_firstName isEqualToString:otherPerson.firstName]) {
return NO;
}
if (![_lastName isEqualToString:otherPerson.lastName]) {
return NO;
}
if (_age != otherPerson.age) {
return NO;
}
return YES;
}
複製代碼
首先判斷了指針是否相等,以後判斷所屬的類。不過在判斷類的時候,須要特別注意有繼承的狀況,若是判斷子類是否與父類相等,就不能這麼粗暴的重寫isEqual方法了,須要根據狀況,自定義一個子類和父類的比較規則。atom
以後就須要實現hash方法了,最早想到的是這樣:spa
- (NSUInterger)hash {
return 1024;
}
複製代碼
這樣實現以後確實能實現兩個EOCPerson
對象的等同性判斷了。但在一些場景中會產生性能問題。好比一個用set實現的collection,collocation在檢索哈希表時,會用哈希值做爲索引。每次向collection添加新對象時,優先會檢查哈希值相同的相關對象,再判斷等同性。因此,若是已經有了10000個對象在set中,此時添加一個新對象,按照上個例子中的實現,就須要遍歷完10000個對象才能完成插入對象的操做。指針
那麼咱們將上述例子作一下改進:code
- (NSUInterger)hash {
NSString *stringToHash = [NSString stringWithFormat:@"%@:%@:%i", _firstName, _LastName, _age];
return [stringToHash hash];
}
複製代碼
這麼作就能避免剛纔所說的問題了,提升set操做時的檢索效率,但字符串操做相對返回一個單一值來講仍然是一個耗時過程,會影響set的性能。
再進一步的優化一下:
- (NSUInterger)hash {
NSUInterger firstNameHash = [_firstName hash];
NSUInterger lastNameHash = [_lastName hash];
NSUInterger ageHash = _age;
return firstNameHash ^ lastNameHash ^ ageHash;
}
複製代碼
這樣既解決了哈希索引的問題,有提升了哈希值生成的效率。因此哈希值的生成有兩個注意點:
在判斷數組的是否等同的時候,須要判斷數組內全部對象都相同,咱們把這個叫作「深度等同性判斷」。但有時候,根據不一樣的場景,不須要判斷全部對象或者對象的全部屬性都同樣,這樣就能夠下降等同性判斷的執行深度。
好比數組的數據是從數據庫裏取出的,這個時候只要判斷數據對象對應的主鍵是否相等,便能判斷這個對象的等同性。