今天看工程代碼,發現原工程中定義了一個 const 常量字符串。并且通過 isEqual 來和這個常量字符串進行比較。
產生了疑問:這也能比較字符串?比較字符串是根據什么來比較的呢?地址?內容?還是什么?
查閱資料發現:當兩個物體有一系列相同的可觀測的屬性時,兩個物體可能是相互相等的或者是等價的。但是這兩個物體的本身又是不同的,它們有各自的本體。而在變成中,一個對象的本體就是它的內存地址。(Equality)
在OC中對于 NSObject 基類來說,isEqual 的實現就是直接比較地址的:
- (BOOL)isEqual:(id)object {
return self == object; // 即指向同一塊地址
}
對于 NSObject 每個子類來說實現 isEqual 這個方法時,都應該要實現以下幾個步驟:
1、實現一個 isEqualTo__ClassName__ 方法,進行實際意義上的值比較。
2、重載 isEqual 方法,此方法進行類和是否為 self 的判斷,如果是 NO 則進行 isEqualTo__ClassName__ 方法判斷。
3、重載 hash 方法
對于 NSObject 的子類來說并不是這么簡單了。如 NSArray 的 isEqualArray的猜想
- (BOOL)isEqualToArray:(NSArray *)array {
? ? if (!array && array.count != self.count) {
? ? ?return NO;
? ? }
? ? for (NSUInteger idx =0; idx < [array count]; idx++) {
? ? ? if (![self[idx] isEqual:array[idx]]) {
? ? ? ? ? ?return NO;
? ? ? ? }
? ?}
? ? ? return YES;
}
- (BOOL)isEqual:(id)object?{
? ? ? ?if (self == object) {
? ? ? ? ?return YES;
? ? ? }
? ? ?if (![object isKindOfClass:[NSArray class]]) {
? ? ? ? ?return NO;
? ? ? }
? ? ?return [self isEqualToArray:object];
}
根據對 NSArray 的猜想即我們可以自定義類來實現 isEqual 方法
.h 定義
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSDate *birthday;
@end
.m 實現
@implementation?Person
- (BOOL)isEqualToPerson:(Person*)person {
? ? if(!person) {
? ? ? return NO;
}
? ? BOOL hasEqualName = (!self.name && !person.name) || ([self.name isEqualToString:person.name]);
? ?BOOL hasEqualBirthday = (!self.birthday && person.birthday) || ([self.birthday isEqualToDate:person.birthday]);
? ?return ?hasEqualName && hasEqualBirthday;
}
- (BOOL)isEqual:(id)object {
? ? ? if (self == object) {
? ? ? ? ?return YES;
? ? ?}
? ? if (![object isKindOfClass:[Person class]]) {
? ? ? ?return NO;
? ? }
? ? return [self isEqualToPerson:object];
}
@end
Person *p1 = [[Person alloc] init];
p1.name = @"Bob";
p1.birthday = [NSDate dateWithTimeIntervalSince1970:1000];
Person *p2 = [[Person alloc] init];
p2.name = @"Bob";
p2.birthday = [NSDate dateWithTimeIntervalSince1970:1000];
if ([p1 isEqual:p2]) {
? ? ?NSLog(@“YES");
}?else?{
? ? NSLog(@"NO");
}
打印的是:YES
還是開頭那句話兩個物體是相互相等的或等價的,但本質又不是相同的。
對于 isEqual 中的第三點重寫 hash 方法
實際上,對于關鍵屬性的散列值進行一個簡單的XOR操作,就能夠滿足在 99% 的情況下的需求了。(Equality)
- (NSUInteger)hash {
return [self.name hash] ^ [self.birthday hash];
}
那為什么要重寫 hash 方法呢?
在 OC 中 NSSet 和 NSDictionary 都是通過 hash table 來進行查找的,從而提高到 O(1)。
用數組和 hash table 進行比較下:
1.?數組把元素存儲在一系列連續的地址當中。
2.hash table 是在內存中分配 n 個位置,然后使用一個函數來計算出某個對象的具體位置。
在 NSDictionary 中通過 key 的 hash 值根據函數快速查找到對應的 value 值。
即在 setObject:forKey: 將 Person 對象作為 key 時就是調用 Person 的 hash 方法,因為 NSDictionary 要判斷是否是同一個對象。 判斷步驟:1。通過 hash 方法的 hash值 判斷,不相同則直接操作。相同進入第二部。2. 根據 isEqual 方法來判斷對象是否相同。(即 hash 方法和 isEqual 方法的 關系了)
在 NSSet 中和 NSDictionary 里相似。 NSSet 中對象是不能重復,則是通過 hash 值 和 isEqual 結合來判斷的。
參考鏈接: