對象等同性
無論我們使用什么語言,總是會出現需要判斷兩個對象是否相等的情況,OC當然也不例外。首先看一段代碼:
NSString *str1 = [[NSString alloc] initWithCString:"equal" encoding:NSUTF8StringEncoding]; NSString *str2 = @"equal"; if(str1 == str2) { NSLog(@"equal"); }
很明顯,在我們開來,str1和str2是“相等的”。但是事實上equal是不會被打印的。這是因為如果我們直接比較兩個對象是否相等,實際上比較的是兩個對象的指針是否相等。
上述代碼中str1和str2是分別指向兩塊不同的內存的,所以肯定不會像等了。
我們稍微修改一下代碼再看看:
NSString *str1 = [[NSString alloc] initWithCString:"equal" encoding:NSUTF8StringEncoding]; NSString *str2 = @"equal"; if([str1 isEqual:str2]) { NSLog(@"equal"); }
注意看加粗語句,我們改用NSObject提供的isEqual方法比較,發現"equal"被打印了出來。因為isEqual在NSString 內部被實現的時候比較的是真正的字符串是否相等!
對象等同性實現
看過上面例子后,現在我們自己創建一個類來進一步說明等同性
#import <Foundation/Foundation.h>@interface EqualObject : NSObject@PRoperty(nonatomic ,strong)NSString *name;@end#import "EqualObject.h"@implementation EqualObject@end
定義了一個EqualObject類,有一個name屬性。
現在我們創建兩個對象來比較一下:
EqualObject *object1 = [EqualObject new]; EqualObject *object2 = [EqualObject new]; if([object1 isEqual:object2]) { NSLog(@"equal"); }
發現代碼運行結束并沒有輸出"equal",原因就在于isEqual方法是需要我們自己實現的。NSObject的isEqual:方法默認是比較兩個對象指向的地址是否相等,這里開辟了兩個對象肯定不想等了。
現在我們添加isEqual:方法的實現:
-(BOOL)isEqual:(id)object{ if([self class] == [object class]) { if(![self.name isEqual:[(EqualObject *)object name]]) { return NO; } return YES; } else { return [super isEqual:object]; }}
這里稍微解釋一下,為什么兩個對象不同類就調用父類的isEqual:這是因為,有的時候我們是可以讓子類等于父類的,我們只需要關注屬性是否相同時可以這樣寫,如果不需要也可以不在父類處理那么久默認不相等了。
現在我們不對name進行賦值操作依然是沒有值打印出來的。
修改客戶端代碼:
EqualObject *object1 = [EqualObject new]; EqualObject *object2 = [EqualObject new]; object1.name = @"xiaoming"; object2.name = @"xiaoming"; if([object1 isEqual:object2]) { NSLog(@"equal"); }
發現這時候在運行就已經相等了。
為類定制等同性方法
我們可以看到NSString除了可以用isEqual比較是否相等意外,還可以使用isEqualToString來比較!這是專為NSString類定制的等同性方法,提供這樣的方法就可以很明確我們實現了該對象的isEqual方法。
下面為EqualObject提供定制的等同性方法,并修改isEqual:方法
- (BOOL)isEqualToEqualObject:(EqualObject *)object{ if(self == object) return YES; if(![self.name isEqualToString:object.name]) return NO; return YES;}- (BOOL)isEqual:(id)object{ if([self class] == [object class]) { return [self isEqualToEqualObject:object]; } else { return [super isEqual:object]; }}
然后客戶端修改
if([object1 isEqualToEqualObject:object2]) { NSLog(@"equal"); }
很順利的"equal"了...
對象hash碼
每一個OC對象內部都是有一個hash碼的,當對象存入集合中(Array,Set,HashTable等),那么他們的hash碼會被當做鍵來決定他們該放入哪一個集合中。
首先我們先看一下集合內部是如何存儲的
hashCode | subCollection |
code1 | value1,value2,value3,value4 |
code2 | value5,value6 |
code3 | value7 |
code4 | value8,value9,value10 |
集合的內部并不像我們所想的那樣,是一個hash表,它將插入的對象根據hashCode來決定放入哪一個子集合。如果要刪除或者比較集合內元素,它首先根據hashCode找到子集合,然后跟子集合的每個元素比較。
所以如果我們的對象的hashCode如果都相同,那么就會出現嚴重的效率問題,
理論上來說,我們確定等同性的兩個對象的hash應該是相同的而不等的兩個對象hash也應該不等,這樣在存入hashTable之類的集合時,就會避免相同對象的重復添加,比如我們兩個對象hash相等,但實際對象不等,那么添加的時候就會被添加到同一subCollection下面。
所以為了避免這種情況,我們盡量自己實現一種避免重復的方式,
這里提供一種,添加一個新屬性age,hash實現如下:
- (NSUInteger)hash{ NSUInteger nameHash = [_name hash]; NSUInteger ageHash = _age; return nameHash ^ ageHash;}
集合中的對象等同性
我們對NSArray調用isEqual方法,它會對集合里的每個對象和另一個集合相同位置的對象進行isEqual:操作,只有全部相等,兩個集合才相等。
這里說一下,集合里面最后添加都是不可變元素,如果是可變性元素會出現不法控制的情況。
比如我們往NSSet里面添加兩個NSMutableArray,一開始兩個array不等,那么set中就有兩個元素。
然后修改一個array使兩個相等,這是set中就會有兩個相等的元素存在!
新聞熱點
疑難解答