第八條 :理解“對象等同性”這一概念
根據(jù)“等同性“(equality)來比較對象是一個非常有用的功能染厅。
不過按照”==“操作符比較出來的結(jié)果未必是我們想要的捌朴,因為該操作比較的是兩個指針本身,而不是其所指的對象庞溜。應(yīng)該使用NSObject協(xié)議中聲明的”isEqual“:方法來判斷兩個對象的等同性革半。
一般來說,兩個類型不同的對象總是不相等(unequal)流码。某些對象提供了特殊的”等同性判斷方法“又官,如果已經(jīng)知道兩個受測對象都屬于同一類,那么就可以使用這種方法漫试。
NSString類實(shí)現(xiàn)了一個自己獨(dú)有的等同性判斷方法赏胚,名叫“isEqualToString:”。傳遞給該方法的對象必須是NSString商虐。調(diào)用該方法比調(diào)用“isEqual”方法快觉阅。因為isEqual方法不知道受測對象的類型。
NSObject協(xié)議中有兩個用于判斷等同行的方法:
-(BOOL)isEqual:(id)object;
-(NSUInteger)hash;
NSObject類對這兩個方法的默認(rèn)實(shí)現(xiàn)是:當(dāng)且僅當(dāng)“指針值”(pointer value)完全相等時秘车,這兩個對象才相等典勇。若想在自定義的對象中,正確復(fù)寫這些方法叮趴,就必須先理解其約定(contract)割笙。
如果isEqual:方法判定兩個對象相等,那么其hash也必須返回同一個值。
但是伤溉,如果兩個對象的hash方法返回同一個值般码,那么“isEqual:”方法未必會認(rèn)為兩者相等。
collection:array乱顾、dictionary板祝、set等數(shù)據(jù)結(jié)構(gòu)的總稱
collection在檢索hash表時,會用對象的哈希碼做索引走净。假如某個collection是用set實(shí)現(xiàn)的券时,那么set可能會根據(jù)哈希碼把對象分裝到不同的數(shù)組中。在向set中添加對象時伏伯,要根據(jù)其哈希碼找到與之相關(guān)的的那個數(shù)組橘洞,依次檢查各個元素,看數(shù)組中已有的對象是否和將要添加的對象相等说搅。如果相等炸枣,那就說明將要添加的對象已經(jīng)在set中了。如果令每個對象都返回hash碼弄唧,那么必然會產(chǎn)生性能問題适肠。
hash方法也可以這樣來實(shí)現(xiàn):
將NSString對象中的所有屬性都放在一個字符串中,然后領(lǐng)hash方法返回該字符串的hash碼套才。這樣做也符合約定迂猴,兩個相同對象會返回相同的hash碼。
但是這樣做還需負(fù)擔(dān)創(chuàng)建字符串的開銷背伴,所以比返回單一值要慢沸毁。
把這種對象放入collection中也會產(chǎn)生性能問題,因為想要添加對象傻寂,必須先計算其hash碼息尺。
另一種計算hash碼的辦法:
-(NSUInteger)hash{
NSUInteger firstNameHash = [_firstName hash];
NSUInteger lastNameHash = [_lastName hash];
NSUInteger ageHash = ?_age;
return firstNameHash ^ lastNameHash ^ ageHash;
}
這種做法既能保持高效,又能使生成的hash碼至少位于一定范圍內(nèi)疾掰,而不會過于頻繁的重復(fù)搂誉。
當(dāng)然,這種算法生成的hash碼還是會碰撞(collision)静檬,不過至少可以保證hash碼有多種可能的取值炭懊。
特定類所具有的等同性判定方法
如果某些特定類需要經(jīng)常判斷等同性,那么可能需要自己來創(chuàng)建等同性判定方法拂檩,因為無須檢測參數(shù)類型侮腹,所以能大大提升檢測速度。
在編寫判定方法時稻励,也應(yīng)一并復(fù)寫“isEqual”方法父阻。后者的常見實(shí)現(xiàn)方法為:如果受測的參數(shù)與接收該消息的對象都屬于同一個類,那么就調(diào)用自己寫的判定方法,否則就交由超類來判斷加矛。
等同性判定的執(zhí)行深度
創(chuàng)建等同性判定方法時履婉,需要決定是根據(jù)整個對象來判斷等同性,還是僅根據(jù)其中幾個字段來判斷斟览。
例如:NSArray的檢測方式為先看兩個數(shù)組所含對象的個數(shù)是否相同毁腿,若相同,則在每個對應(yīng)位置的兩個對象身上調(diào)用其“isEqual:”方法趣惠。如果對應(yīng)位置上的對象均相等狸棍,那么這兩個數(shù)組就相等身害,這叫做“深度等同性判定”(deep equality)味悄。
不過,有時候無須將所有的數(shù)據(jù)逐個比較塌鸯,只根據(jù)其中部分?jǐn)?shù)據(jù)即可判明二者是否等同侍瑟。
容器中可變類的等同性
如果在容器中放入可變類對象的時候,把某個對象放入collection中后丙猬,就不應(yīng)再改變其hash碼了涨颜。collection會把各個對象按照其hash碼分裝到不同的“箱子數(shù)組”中。如果對象的hash碼在放入”箱子“之后又變了茧球,那么其所在的這個箱子對他來說就是”錯誤“的庭瑰。
要想解決這個問題,要保證抢埋,對象的hash碼弹灭,不是根據(jù)對象的可變部分計算來的。
或者保證放入collection之后就不再改變對象內(nèi)容了揪垄。
【要點(diǎn)】
1.若想要檢測對象的”對象的等同性“穷吮,請?zhí)峁眎sEqual:“與hash方法
2.相同的對象必須具有相同的hash碼,但是兩個hash碼相同的對象卻未必相同
3.不要盲目地逐個檢測每條屬性饥努,而是應(yīng)該依照具體需求來制定檢測方案
4.編寫hash方法時捡鱼,應(yīng)該使用計算速度快而且hash碰撞幾率低的算法