高效編寫代碼的方法(六):判斷對象相等

isEqual

比較對象是否相等,在OC中目前有兩種方式:
==isEqual:方法。
對于==方法,對于指針類對象的判斷,其實挺雞肋的,系統只是簡單的進行指針地址的對比。當我們想更“深入”地對比對象的時候,顯然==是不能滿足要求的。
對于我們在框架中常用的類型對象,比如NSString,NSArray類等,系統對于這些類,已經實現了一個isEqual方法可以幫助我們去對對象做比較。對于NSString類,還有一個特性的方法:

- (BOOL)isEqualToString:(NSString *)aString;

由此增加這個相等方法對于類型的判斷。提高對比效率,我們在自己實現isEqualTo方法是也要加強類型判斷。

下面兩個方法是比較對象時最為關鍵的兩個方法:

- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;

默認的isEqual實現與==類似,都是直接比較指針指向地址是否相同。
我們在自己重寫isEqual時也應該重寫hash方法,由此保證,相同的兩個對象返回的hash是一樣的。但是有相同hash的對象,卻又不一定的相等的。

下面是一個重寫isEqual的例子:

- (BOOL)isEqual:(id)object
{
    if (self == object) {
        return YES;
    }
    if ([self class] != [object class]) {
        return NO;
    }
    Teacher *anotherTeacher = (Teacher *)object;
    if (![self.name isEqualToString:anotherTeacher.name]) {
        return NO;
    }
    if (![self.workNumber isEqualToString:anotherTeacher.workNumber]) {
        return NO;
    }
    return YES;
}

以上是一個Teacher類的對比方法,首先對比指針,如果self 和object相同,那么必定相等。然后我們在做類型判斷,如果兩者類型都不相等,那么必定不相等。但是如果有一個類叫JohnTeacher,固定name為john,是Teacher的子類,此時如果一個Teacher的name屬性為john,那么兩者是可能相等的,此時在isEqual方法實現中,就要對類型的判斷做更加細致的判斷,建議使用isKindOfClass進行判斷。最后就是進行對象的各屬性是否相等的判斷了,如果屬性都相等,即可認為兩個對象相等。

為保證相同對象都有相同的hash值這樣一個規定,重寫hash也是必須的。
比如可以這樣:

-(NSUInteger)hash{
    return 1337;
}

這樣來說,相同的類的對象都有同一個Hash。

但是在一個集合使用的場景下,比如向一個NSSet(不允許對象重復)中添加對象。那個這個set在添加對象的時候,就會先去對比Hash值,發現hash相同,那么會再去調用isEqual去進一步判斷對象是否相等,如果添加的對象過多,那么每一次都會調用isEqual,造成執行效率低。

另一種hash的實現方法如下:

-(NSUInteger)hash{
    NSString *stringToHash = [NSString stringWithFormat:@"%@:%@",_name,_workNumber];
    return [stringToHash hash];
}

對由對象屬性拼接而成的字符串進行一次hash計算,最后返回這個值,由此可以確保相同對象,hash值一定是一樣的。但是這種方法肯定不如返回規定值來的快。

最后總結一個最合適的方法:

-(NSUInteger)hash{
    NSUInteger nameHash = [_name hash];
    NSUInteger workNumberHash = [_workNumber hash];
    return nameHash ^ workNumberHash;
}

以上是一種相對折中的方法,hash值不會單一,執行量也不算太大。

規定類型的判斷方法

對于NSString來說,isEqualToString肯定要比isEqual方法來的更易讀,類型的規定也更加的明確。
所以我們也可以實現自己的強類型判斷的方法:

- (BOOL)isEqualToTeacher:(Teacher *)anotherTeacher
{
    if (self == anotherTeacher) {
        return YES;
    }
    if (![self.name isEqualToString:anotherTeacher.name]) {
        return NO;
    }
    if (![self.workNumber isEqualToString:anotherTeacher.workNumber]) {
        return NO;
    }
    return YES;
}

如此,在isEqual方法中,我們也可以寫的更簡單一點:

- (BOOL)isEqual:(id)object
{
    if ([self class] == [object class]) {
        return [self isEqualToTeacher:object];
    }else{
        return [super isEqual:object];
    }
}

深相等與淺相等

以上isEqual中我們都對比了對象的每一個屬性是否相等,這樣的一種詳盡的對比方式可以稱為“深相等”

但是某些時候,比如Teacher,我們可以給他分配一個標識屬性,比如identifier。這個identifier對外只讀,并且每個對象identifier不相同,由此我們可以直接對比identifier即可,該方法為“淺相等”。

可變對象的對比

下面這個例子,中文有點難以描述,大致意思是可變對象的hash會隨對象的改變而改變,將對象加入一個集合中時可能會引發問題。直接上代碼:

 NSMutableSet *set = [[NSMutableSet alloc] init];
    NSMutableArray *arrayA = [@[@1,@2]  mutableCopy];
    [set addObject:arrayA];
    NSLog(@"1: set = %@", set);
    
    NSMutableArray *arrayB = [@[@1,@2]  mutableCopy];
    [set addObject:arrayB];
    NSLog(@"2: set = %@", set);
    
    NSMutableArray *arrayC = [@[@1]  mutableCopy];
    [set addObject:arrayC];
    NSLog(@"3: set = %@",set);
    [arrayC addObject:@2];
    NSLog(@"4: set = %@",set);
    
    NSSet *setB = [set copy];
    NSLog(@"5: setB = %@",setB);

整理后的打印結果:


LogResult

可以觀察到第四個輸出結果set = {(1,2),(1,2)},顯然與Set的定義相違背(不能有相同對象),而且更奇怪的是setB 拷貝了set后變成了{(1,2)}
所以我們在遇到這樣的情況時,不要修改被加入集合后的對象,否則導致像以上代碼那樣的混亂。

總結

  • 1 如果想自定義對比對象的方法,重寫isEqual和hash方法。
  • 2 相同對象,hash必須相同,但擁有相同hash的對象不一定本身相同。
  • 3 有選擇的對比對象屬性,而不是全部對比(針對有特殊標識屬性的對象)
  • 4 hash方法要可讀并高效
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容