国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > iOS > 正文

iOS開發之isEqual與hash!

2019-11-06 10:03:20
字體:
來源:轉載
供稿:網友

目錄

為什么要有isEqual方法?

如何重寫自己的isEqual方法?

為什么要有hash方法?

hash方法什么時候被調用?

hash方法與判等的關系?

如何重寫自己的hash方法?

為什么要有isEqual方法?

isEqual方法的作用大家肯定是知道的:

判斷兩個對象是否相等

但是判斷相等不是已經有==運算符了么, 為什么還要isEqual方法?

這是因為:

對于基本類型, ==運算符比較的是值; 對于對象類型, ==運算符比較的是對象的地址(即是否為同一對象)

注意: 上述==運算符的說明適用于Objective-C和java等不支持運算符重載的語言, 支持運算符重載的語言有C++

所以要理清==運算符和isEqual方法的區別, 問題就集中在

什么叫比較對象的地址, 什么叫比較對象

我們通過下面的例子來說明這個問題

UIColor *color1 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];UIColor *color2 = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];NSLog(@"color1 == color2 = %@", color1 == color2 ? @"YES" : @"NO");NSLog(@"[color1 isEqual:color2] = %@", [color1 isEqual:color2] ? @"YES" : @"NO");

打印結果如下

color1 == color2 = NO[color1 isEqual:color2] = YES

從上面的例子可以看出, ==運算符只是簡單地判斷是否是同一個對象, 而isEqual方法可以判斷對象是否相同, 例如UIColor對象表示的color是否相同

如何重寫自己的isEqual方法?

對于Cocoa Framework中定義的類型, 例如上面例子中的UIColor, isEqual方法已經實現好了

常見類型的isEqual方法還有NSString isEqualToString / NSDate isEqualToDate / NSArray isEqualToArray / NSDictionary isEqualToDictionary / NSSet isEqualToSet, 更多參考Equality

但對于自定義類型來說, 通常需要重寫isEqual方法

通過下面的例子, 我們來看看重寫isEqual方法的正確姿勢

<!--more-->

首先定義Person類如下

@interface Person : NSObject@PRoperty (nonatomic, copy) NSString *name;@property (nonatomic, strong) NSDate *birthday;@end

Person類中實現的isEqual方法如下

- (BOOL)isEqual:(id)object { if (self == object) { return YES; } if (![object isKindOfClass:[Person class]]) { return NO; } return [self isEqualToPerson:(Person *)object];}- (BOOL)isEqualToPerson:(Person *)person { if (!person) { return NO; } BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name]; BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday]; return haveEqualNames && haveEqualBirthdays;}

上述代碼主要步驟如下

Step 1: ==運算符判斷是否是同一對象, 因為同一對象必然完全相同

Step 2: 判斷是否是同一類型, 這樣不僅可以提高判等的效率, 還可以避免隱式類型轉換帶來的潛在風險

Step 3: 通過封裝的isEqualToPerson方法, 提高代碼復用性

Step 4: 判斷person是否是nil, 做參數有效性檢查

Step 5: 對各個屬性分別使用默認判等方法進行判斷

Step 6: 返回所有屬性判等的與結果

isEqual的實現并不復雜, 但是從代碼質量(效率, 安全, 復用)來說, 上述實現仍然值得仔細學習和借鑒

除了上面的最佳實踐, 還有一種最不佳實踐

@implementation NSDate (Approximate)- (BOOL)isEqual:(id)object { return YES;}@end

這里的isEqual方法一直返回YES

NSLog(@"[self.date1 isEqual:@/"hello/"] = %@", [self.date1 isEqual:@"hello"] ? @"YES" : @"NO");

打印結果如下

[self.date1 isEqual:@"hello"] = YES

這個有趣的實驗說明: 對象的判等可以完全由您決定, 即使兩個完全不同的對象

為什么要有hash方法?

這個問題要從Hash Table這種數據結構說起

首先我們看下如何在數組中查找某個成員

Step 1: 遍歷數組中的成員

Step 2: 將取出的值與目標值比較, 如果相等, 則返回該成員

在數組未排序的情況下, 查找的時間復雜度是O(array_length)

為了提高查找的速度, Hash Table出現了

當成員被加入到Hash Table中時, 會給它分配一個hash值, 以標識該成員在集合中的位置

通過這個位置標識可以將查找的時間復雜度優化到O(1), 當然如果多個成員都是同一個位置標識, 那么查找就不能達到O(1)了

重點來了:

分配的這個hash值(即用于查找集合中成員的位置標識), 就是通過hash方法計算得來的, 且hash方法返回的hash值最好唯一

和數組相比, 基于hash值索引的Hash Table查找某個成員的過程就是

Step 1: 通過hash值直接找到查找目標的位置

Step 2: 如果目標位置上有多個相同hash值得成員, 此時再按照數組方式進行查找

hash方法什么時候被調用?

帶著這個問題, 我們來看下面的例子

Person *person1 = [Person personWithName:kName1 birthday:self.date1];Person *person2 = [Person personWithName:kName2 birthday:self.date2];NSMutableArray *array1 = [NSMutableArray array];[array1 addObject:person1];NSMutableArray *array2 = [NSMutableArray array];[array2 addObject:person2];NSLog(@"array end -------------------------------");NSMutableSet *set1 = [NSMutableSet set];[set1 addObject:person1];NSMutableSet *set2 = [NSMutableSet set];[set2 addObject:person2];NSLog(@"set end -------------------------------");NSMutableDictionary *dictionaryValue1 = [NSMutableDictionary dictionary];[dictionaryValue1 setObject:person1 forKey:kKey1];NSMutableDictionary *dictionaryValue2 = [NSMutableDictionary dictionary];[dictionaryValue2 setObject:person2 forKey:kKey2];NSLog(@"dictionary value end -------------------------------");NSMutableDictionary *dictionaryKey1 = [NSMutableDictionary dictionary];[dictionaryKey1 setObject:kValue1 forKey:person1];NSMutableDictionary *dictionaryKey2 = [NSMutableDictionary dictionary];[dictionaryKey2 setObject:kValue2 forKey:person2];NSLog(@"dictionary key end -------------------------------");

為了看清楚hash方法是否被調用, 我們重寫hash方法如下

- (NSUInteger)hash { NSUInteger hash = [super hash]; NSLog(@"hash = %ld", hash); return hash;}

打印結果如下

person1 == person2 = NO[person1 isEqual:person2] = NOisEqual end -------------------------------array end -------------------------------hash = 7809196951631946839hash = 7809196951631946839hash = 7809191961023760480hash = 7809191961023760480set end -------------------------------dictionary value end -------------------------------hash = 7809196951631946839hash = 7809196951631946839hash = 7809191961023760480hash = 7809191961023760480dictionary key end -------------------------------

從打印結果可以看到:

hash方法只在對象被添加至NSSet和設置為NSDictionary的key時會調用

NSSet添加新成員時, 需要根據hash值來快速查找成員, 以保證集合中是否已經存在該成員

NSDictionary在查找key時, 也利用了key的hash值來提高查找的效率

hash方法與判等的關系?

hash方法主要是用于在Hash Table查詢成員用的, 那么和我們要討論的isEqual()有什么關系呢?

為了優化判等的效率, 基于hash的NSSet和NSDictionary在判斷成員是否相等時, 會這樣做

Step 1: 集成成員的hash值是否和目標hash值相等, 如果相同進入Step 2, 如果不等, 直接判斷不相等

Step 2: hash值相同(即Step 1)的情況下, 再進行對象判等, 作為判等的結果

簡單地說就是

hash值是對象判等的必要非充分條件

如何重寫自己的hash方法?

很多人在iOS開發中, 都是這么重寫hash方法的

- (NSUInteger)hash { return [super hash];}

這樣寫有問題么? 帶著這個問題, 我們先來看下[super hash]的值到底是什么

Person *person = [[Person alloc] init];NSLog(@"person = %ld", (NSUInteger)person);NSLog(@"[person1 getSuperHash] = %ld", [person getSuperHash]);

打印結果如下

person = 140643147498880[person1 getSuperHash] = 140643147498880

由此可以看出, [super hash]返回的就是該對象的內存地址

聯想到前面對hash值唯一性的要求, 使用對象的內存地址作為hash值不是很好么?

別急, 我們添加如下兩個對象到NSSet中試試

Person *person1 = [Person personWithName:kName1 birthday:self.date1];Person *person2 = [Person personWithName:kName1 birthday:self.date1];NSLog(@"[person1 isEqual:person2] = %@", [person1 isEqual:person2] ? @"YES" : @"NO");NSMutableSet *set = [NSMutableSet set];[set addObject:person1];[set addObject:person2];NSLog(@"set count = %ld", set.count);

此時打印結果如下

[person1 isEqual:person2] = YESset count = 2

isEqual相等的兩個對象都加入到了NSSet中(set count = 2), 所以直接返回[super hash]是不正確的

那么hash方法的最佳實踐到底是什么呢?

大神Mattt Thompson在Equality中給出的結論就是

In reality, a simple XOR over the hash values of critical properties is sufficient 99% of the time(對關鍵屬性的hash值進行位或運算作為hash值)

對于上面Person類的hash方法實現如下

- (NSUInteger)hash { return [self.name hash] ^ [self.birthday hash];}

更多關于位運算的討論, 參考Implementing Equality and Hashing

參考

Equality

Implementing Equality and Hashing

NSObject的hash方法

淺拷貝深拷貝以及 Hash/Equal


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 河源市| 福泉市| 华安县| 和林格尔县| 顺平县| 陵川县| 射洪县| 万年县| 新泰市| 万全县| 曲沃县| 池州市| 陆河县| 岢岚县| 土默特左旗| 马鞍山市| 霍邱县| 修文县| 陇南市| 石泉县| 江阴市| 榆中县| 囊谦县| 汶上县| 同心县| 五家渠市| 宁阳县| 长岭县| 铁岭市| 承德市| 宜良县| 平原县| 崇义县| 锦州市| 延寿县| 清涧县| 康乐县| 商洛市| 仁寿县| 保山市| 佛学|