歡迎到我的 個人博客 http://liumh.com 瀏覽此文
使用 ==
比較 iOS 中的對象時风纠,比較的是對象的指針。例如有如下比較時:
NSString *str = @"origin1";
NSString *strCopy = [str copy];
NSString *str1 = [NSString stringWithFormat:@"origin%@", @1];
BOOL equalA = (str == strCopy);
BOOL equalB = (str == str1);
BOOL equalC = [str isEqualToString:str1]; /**< equalC is YES */
BOOL equalD = [str isEqual:str1]; /**< equalD is YES */
equalA
的值會是 YES
, 而 equalB
的值會是 NO
牢贸,雖然 str
竹观、str1
潜索、strCopy
指針所指的對象的值都是 origin1
。
比較對象時竹习,我們應該使用 NSObject
協(xié)議中聲明的 isEqual:
方法來判斷兩個對象的等同性誊抛。一般來說,兩個類型不同的對象總是不相等的整陌。
NSObject
協(xié)議中有兩個判斷對象等同性的關鍵方法:
- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;
NSObject
對這兩個類的默認實現(xiàn)是:當且僅當其 “指針值” 完全相等時,這兩個對象才相等泌辫。若想在自定義對象中正確覆寫這兩個方法随夸,那么就必須先理解其約定: 如果 isEqual:
方法判斷兩個對象相等,那么其 hash
方法必須返回同一個值宾毒,但是如果兩個對象的 hash
方法返回同一個值,isEqual:
方法未必會認為二者相等殿遂。
假如有如下類:
@interface ACLStudent : NSObject
@property (nonatomic, assign, readonly) NSUInteger studentId;
@property (nonatomic, copy, readonly) NSString *firstName;
@property (nonatomic, copy, readonly) NSString *lastName;
@end
對于這樣的自定義對象伍俘,可以像 NSString
的 - (BOOL)isEqualToString:(NSString *)aString
方法一樣,創(chuàng)建一個屬于該類的特定等同性方法癌瘾。同時需要注意該類對象的使用場景觅丰,例如這里,我們在判定兩個類對象是否相等時妨退,不需要對象中的所有字段都判定相等,只需要其 studentId
相等就可判定兩個對象表示的是同一個學生對象咬荷。方法實現(xiàn)像下面這樣:
- (BOOL)isEqualToStudent:(ACLStudent *)student {
if (self == student) {
return YES;
}
if (self.studentId == student.studentId) {
return YES;
}
return NO;
}
建議同時實現(xiàn)其 isEqual:
方法冠句,像下面這樣:
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if ([self class] == [object class]) {
return [self isEqualToStudent:object];
} else {
return [super isEqual:object];
}
}
首先比較其指針值幸乒,假如指針值相等懦底,則對象相等罕扎,然后判斷傳入對象的類型聚唐,如果是類的類型腔召,則調(diào)用類特定的等同性方法杆查,否則交由父類來判斷臀蛛。
同時不要忘記覆寫 - (NSUInteger)hash
方法:
- (NSUInteger)hash {
return self.studentId;
}
在覆寫類的 hash
方法時亲桦,要注意其有較高的執(zhí)行效率浊仆,又能使生成的哈希碼至少位于一定的范圍內(nèi),不至于頻繁的重復抡柿。編寫 hash
方法時舔琅,可用當前的對象做實驗,以便在減少哈希碼碰撞頻度與降低其運算復雜度之間取舍沙绝。
假如類的
hash
方法計算值過于頻繁的重復鼠锈,在collection
中使用這種對象將會產(chǎn)生性能問題闪檬,因為collection
在檢索哈希表時购笆,會用對象的哈希碼做索引粗悯。假如某個collection
是用set
實現(xiàn)的同欠,那么set
可能會根據(jù)哈希碼把對象分裝到不同的數(shù)組中横缔。在向set
中添加新對象時,要根據(jù)哈希碼找到與之相關的那個數(shù)組衫哥,依次檢查其中的各個元素茎刚,看數(shù)組中已有的對象是否和將要添加的對象相等。如果相等膛锭,那就說明要添加的對象已經(jīng)在set
中了。由此可知蚊荣,如果令每個對象的都返回相同的哈希碼初狰,那么在set
中已有 1000000 個對象的情況下互例,若是繼續(xù)向其中添加對象奢入,則需要將這 1000000 個對象全部掃描一遍媳叨。
例如下面兩種實現(xiàn)方法, 方法1:
- (NSUInteger)hash {
NSString *stringToHash = [NSString stringWithFormat:@"%@:%@:%@", @(self.studentId), self.firstName, self.lastName];
return [stringToHash hash];
}
方法2:
- (NSUInteger)hash {
NSUInteger firstNameHash = [self.firstName hash];
NSUInteger lastNameHash = [self.lastName hash];
return firstNameHash ^ lastNameHash ^ self.studentId;
}
方法2 比 方法1 更好腥光,減少了創(chuàng)建字符串的開銷肩杈,同時其哈希值不至于頻繁重復柴我。
注意扩然,我們把對象放入 collection
之后,就不應再改變其哈希碼了夫偶。前面講過界睁,collection
會把各個對象按照其哈希碼分裝到不同的"箱子數(shù)組"中。如果某個對象在放入"箱子"之后哈希碼又變了兵拢,那么其現(xiàn)在所處的箱子對它來說是"錯誤"的。要解決這個問題说铃,需要確保哈希碼不是根據(jù)對象的“可變部分”(mutable portion)計算出來的访惜,或者保證放入 collection
之后就不再改變對象的內(nèi)容了。