解析和重寫NSObject 的isEqual和hash方法

前言

NSObject給我們提供了-isEqual-hash方法,下面我們具體介紹一下這兩個方法主要功能是什么,會在什么時候被調(diào)用,如何根據(jù)自己定制化的需求進(jìn)行重寫.

isEqual

我們先查看一下方法的聲明- (BOOL)isEqual:(id)object;,拿另一個對象與當(dāng)前object進(jìn)行對比,返回一個布爾值,來確認(rèn)這兩個對象是否相等.
在進(jìn)一步解釋這個方法之前,我們先看一下相等的定義.

什么是相等

我們知道==運算符和isEqual都可以用來判斷相等,他們有什么區(qū)別呢?
我們下面對相等進(jìn)行一些定義,在不同的條件下,我們對相等的定義也會發(fā)生變化,大致分為以下幾種

  1. 內(nèi)存地址相等
  2. 自定義的某些屬性相等

內(nèi)存地址相等,是說這是兩個完全相等的對象
某些屬性相等,這是需要我們關(guān)注和擴(kuò)展的部分,自定義相等的條件

系統(tǒng)有哪些自定義的相等
  • NSAttributedString -isEqualToAttributedString:
  • NSData -isEqualToData:
  • NSDate -isEqualToDate:
  • NSDictionary -isEqualToDictionary:
  • NSHashTable -isEqualToHashTable:
  • NSIndexSet -isEqualToIndexSet:
  • NSNumber -isEqualToNumber:
  • NSOrderedSet -isEqualToOrderedSet:
  • NSSet -isEqualToSet:
  • NSString -isEqualToString:
  • NSTimeZone -isEqualToTimeZone:
  • NSValue -isEqualToValue:

如何自定義-isEqual

假設(shè)我們有一個Person

@interface Person
@property(nonatomic, copy) NSString *firstName;
@property(nonatomic, copy) NSString *secondName
@property(nonatomic, strong) NSDate *birthday;
@end

我們先自定義一下相等的概念,這里我們舉例,如果firstNamesecondName相等,就視為person相等.
下面要做的就是先增加自定義的相等方法:- (BOOL)isEqualToPerson:(Person *)person;
.m的實現(xiàn)如下

@implementation Person

- (BOOL)isEqualToPerson:(Person *)person {
  if (!person) {
    return NO;
  }

  BOOL isFirstNameEqual = (!self.firstName && !person.firstName) || [self.firstName isEqualToString:person.firstName];
  BOOL isSecondNameEqual = (!self.secondName && !person.secondName) || [self.secondName isEqualToString:person.secondName];

  return isFirstNameEqual && isSecondNameEqual;
}

#pragma mark - NSObject

- (BOOL)isEqual:(id)object {
  if (self == object) {
    return YES;
  }

  if (![object isKindOfClass:[Person class]]) {
    return NO;
  }

  return [self isEqualToPerson:(Person *)object];
}

- (NSUInteger)hash {
  return [self.firstName hash << 8] ^ [self.secondName hash];
}

下面分步解析一下:

  1. 重寫父類的isEqual方法,首先判斷是否內(nèi)存地址相等self == object
  2. 判斷![object isKindOfClass:[Person class]]如果Class不相等則直接返回NO
  3. 調(diào)用我們自定義的判等方法- (BOOL)isEqualToPerson:(Person *)person.
isEqual什么時候會被調(diào)用
  1. 我們可以直接調(diào)用isEqual方法來判斷兩個對象是否相等
  2. NSArraycontainObject:方法,會遍歷數(shù)組的元素,并通過isEqual來判斷是否相等
  3. NSSetcontainObject:方法,會先調(diào)用-hash,如果-hash不相等,直接返回false,如果hash相等,則會再調(diào)用isEqual

說到這里問題來了,什么是-hash方法,它的作用是什么?

hash方法

- (NSUInteger)hash返回一個整數(shù),這個數(shù)代表的就是當(dāng)前對象的哈希值
有一個很重要的規(guī)范 : 如果兩個對象相等,他們的hash值必須相等, 如果某個類自定義了isEqual方法,并且這個類的實例有可能會被加入到集合中,一點要確保hash方法被重新定義

和數(shù)組把元素存儲在一系列連續(xù)的地址中不同锌妻,哈希算法使得 NSSetNSDictionary 能夠非持粘椋快速地(O(1)) 進(jìn)行元素查找,哈希表會在內(nèi)存中分配n個位置呼渣,然后使用一個函數(shù)來計算出位置范圍之內(nèi)的某個具體位置.
在數(shù)組和hash表中要判斷一個元素是不是存在的算法和效率是不一樣的凭戴,數(shù)組需要對數(shù)組中每個元素的位置都進(jìn)行檢查,hash有一個更快速的查找方式.

一個好的 hash函數(shù)在不需要太多計算量的情況下陷谱,可以使得生成的位置分布接近于均勻分布,當(dāng)兩個不同的對象計算出相同的散列值時鲫惶,我們稱其為發(fā)生了 哈希碰撞 鞍泉。當(dāng)出現(xiàn)碰撞時疑苫,哈希表會從碰撞產(chǎn)生的位置開始向后尋找,把新的元素放在第一個可供放置的位置,隨著哈希表變得越來越致密畔柔,發(fā)生碰撞的可能性也會隨之增加氯夷,導(dǎo)致查找可用位置花費的時間也會增加(這也是為什么我們希望哈希函數(shù)的結(jié)果分布更接近于均勻分布).
大家對于哈希碰撞哈希算法有一個基本的概念就可以,這一塊之后會單獨拿出來進(jìn)行分析,敬請期待.好了,我們我們繼續(xù)針對我們上面的isEqual需求進(jìn)行講解.

自定義-hash方法

如果兩個對象相等,他們的hash值必須相等, 如果某個類自定義了isEqual方法,并且這個類的實例有可能會被加入到集合中,一點要確保hash方法被重新定義
自定義了兩個對象相等的規(guī)則,那么hash要做的是保證在規(guī)則下了個對象的hash值要相等.
我們可以通過

- (NSUInteger)hash {
  //不完善的示例
  return [self.firstName hash] ^ [self.secondName hash];
}

來實現(xiàn),但是為什么要對firstName進(jìn)行位移呢?
上面我們說過,hash的設(shè)計是為了快速查找,要盡可能的避免hash沖突,也就是不滿足isEqueal的兩個元素,盡量hash不相等,在設(shè)計hash的時候要考慮,是否會比較輕易的使得兩個不等的對象hash值相等,如果是,那么hash算法就要重新設(shè)計.
對于上面的示例來說,john smithsmith john就會有問題,雖然無法避免hash沖突,但是不應(yīng)該這么輕易沖突,為了解決這個易見的hash沖突,可以使用以下

- (NSUInteger)hash {
  return [self.firstName hash << 8] ^ [self.secondName hash];
}

總結(jié)

通過以上的講解和示例,我們已經(jīng)可以實現(xiàn)自定義isEqualhash方法了.
對于hash方法,我們提到我們希望哈希函數(shù)的結(jié)果分布更接近于均勻分布,也就是在避免顯而易見的哈希沖突前提下,使得哈希算法在我們現(xiàn)有的范圍內(nèi)有一定的沖突,目的是為了快速查找,這一塊內(nèi)容對于本篇來說有一點超綱,如果你感興趣,可以繼續(xù)關(guān)注我之后的文章,我會針對哈希沖突進(jìn)行一個比較全面的分析.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市靶擦,隨后出現(xiàn)的幾起案子腮考,更是在濱河造成了極大的恐慌雇毫,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踩蔚,死亡現(xiàn)場離奇詭異棚放,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)寂纪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門席吴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赌结,“玉大人捞蛋,你說我怎么就攤上這事〖硪Γ” “怎么了拟杉?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長量承。 經(jīng)常有香客問我搬设,道長,這世上最難降的妖魔是什么撕捍? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任拿穴,我火速辦了婚禮,結(jié)果婚禮上忧风,老公的妹妹穿的比我還像新娘默色。我一直安慰自己,他們只是感情好狮腿,可當(dāng)我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布腿宰。 她就那樣靜靜地躺著,像睡著了一般缘厢。 火紅的嫁衣襯著肌膚如雪吃度。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天贴硫,我揣著相機(jī)與錄音椿每,去河邊找鬼。 笑死英遭,一個胖子當(dāng)著我的面吹牛拖刃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贪绘,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼兑牡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了税灌?” 一聲冷哼從身側(cè)響起均函,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亿虽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后苞也,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洛勉,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年如迟,在試婚紗的時候發(fā)現(xiàn)自己被綠了收毫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡殷勘,死狀恐怖此再,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玲销,我是刑警寧澤输拇,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站贤斜,受9級特大地震影響策吠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘩绒,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一猴抹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧锁荔,春花似錦蟀给、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嘱丢,卻和暖如春薪介,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背越驻。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工汁政, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缀旁。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓记劈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親并巍。 傳聞我的和親對象是個殘疾皇子目木,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容