1.理解“對象等同性”這一概念
在OC中我們可以通過“==”運算符和方法進行兩個對象是否相等的判斷,“==”只比較兩個對象的指針是否相等,而利用方法可以比較兩個對象的內容是否相等。
看如下代碼:
NSString * str = @"xiao 113";
NSString * str1 = [NSString stringWithFormat:@"xiao %d",113];
BOOL res = (str == str1);//res == NO
BOOL res1 = [str isEqual:str1];//res1 == YES
BOOL res2 = [str isEqualTo:str1];//res2 == YES
BOOL res3 = [str isEqualToString:str1];//res3 = YES
上面簡單介紹了一下,對象等同性判斷的兩種方式,下面開始一段有意思的學習
首先說明在比較兩個對象的時候,使用isEqual:
方法默認是比較兩個對象的指針是否相等,在我們平常的開發中可能會需要比較兩個對象的每個字段是否相等,來作為對象是否相等的判斷依據。
在這個時候我們就可以從寫isEqual:
方法來實現:
@interface Father : NSObject
@property (nonatomic,copy) NSString * name1;
@property (nonatomic,copy) NSString * name2;
@end
@implementation Father
- (BOOL)isEqual:(id)object{
if (self == object) {
return YES;
}
if ([self class] != [object class]) {
return NO;
}
Father * father = (Father *)object;
if (![_name1 isEqualToString:father.name1]) {
return NO;
}
if (![_name2 isEqualToString:father.name2]) {
return NO;
}
return YES;
}
@end
Father * father1 = [[Father alloc]init];
Father * father2 = [[Father alloc]init];
father1.name1 = @"xiao";
father1.name2 = @"nihao";
father2.name1 = @"xiao";
father2.name2 = @"nihao";
BOOL res = [father1 isEqual:father2];
NSLog(@"res---%d",res);//重寫之前res為0,重寫之后res為1
這是最簡單對象的比較,在數組字典的比較中還有一個很重要的函數:
- (NSInteger)hash;
在有的時候我們可能需要比較數組字典當中的內容是否相等,此時為了實現- (Bool)isEqual:
方法,我們需要重寫- (NSInteger)hash;
方法,具體看代碼:
第一種方式:
- (NSUInteger)hash{
return 1337;
}
這種方式會造成嚴重的性能問題,比如在集合中有大量元素的時候,再添加新元素前,集合在檢索hash表的時候要遍歷集合中的每一個元素,這是十分耗性能的。
第二種方式:
- (NSUInteger)hash{
NSString * str = [NSString stringWithFormat:@"%@:%@",_name1,_name2];
return [str hash];
}
這種方式雖然解決了性能的問題,但是會額外增加創建字符串的開銷,肯定還有更好的辦法。
第三種方式:
- (NSUInteger)hash{
NSInteger name1Hash = [_name1 hash];
NSInteger name2Hash = [_name2 hash];
return name1Hash ^ name2Hash;
}
這種方式基本改變了前兩種方式的不足,但也會存在hash碰撞的風險,可以根據自己實際需求,在性能和減少碰撞之間做取舍。
注意:以上三種方式,都改變了- (Bool)isEqual:
方法的判斷形式,這在實際應用中也會有一些注意點,比如:
Father * father1 = [[Father alloc]init];
Father * father2 = [[Father alloc]init];
father1.name1 = @"xiao";
father1.name2 = @"nihao";
father2.name1 = @"xiao";
father2.name2 = @"nihao";
NSMutableSet * set = [[NSMutableSet alloc]init];
//father1和father2的所有字段是相同的
[set addObject:father1];
[set addObject:father2];
//set中并沒有添加father2,這就是改變判別方式后需要引起注意的
NSLog(@"%@",set);
在自定義類中的等同性判定方法
- (BOOL)isEqualToFather:(Father *)otherFather{
if (self == otherFather)
{
return YES;
}
if (![_name1 isEqualToString:otherFather.name1])
{
return NO;
}
if (![_name2 isEqualToString:otherFather.name2]) {
return NO;
}
return YES;
}
- (BOOL)isEqual:(id)object{
if ([self class] == [object class])
{
return [self isEqualToFather:(Father *)object];
}
else
{
return [super isEqual:object];
}
}
這樣做的目的,一是可以按照自己的比較方式進行對象的比較,二是可以很好地封裝,使代碼簡潔。
此外我們在比較兩個對象的時候,還應該看實際需求來確定比較執行的深度,比如我們在比較數組的時候,先確定數組的長度是否相等,在長度相等的情況下,在比較每個數組元素是否相等。但有的時候我們為了比較兩個數組是否相等,我們可以比較特定的變量,比如數組的字段來自于數據庫,我們就可以比較主key的值來判斷兩個數組的內容是否相等。
在可變容器中添加可變對象有的時候會出現問題,下面舉例說明:
NSMutableArray * arrA = [NSMutableArray arrayWithObjects:@1,@2, nil];
NSMutableArray * arrB = [NSMutableArray arrayWithObjects:@1, nil];
NSMutableSet * set = [NSMutableSet set];
[set addObject:arrA];
NSLog(@"1---%@",set);
[set addObject:arrB];
NSLog(@"2---%@",set);
[arrB addObject:@2];
[set addObject:arrB];
NSLog(@"3---%@",set);
NSLog(@"4---%@",[set copy]);
附上打印結果:
從以上結果我們可以看出,在集合中添加可變對象的時候,在改變對象的內容的時候,對象在集合中相應的hash值也會相應的發生改變,這樣的話就出現了再集合中出現了相同的元素,但是這是不允許的在set的語法中。但是當我們copy的時候,set中就會變一個元素,由此我們可以知道,setcopy的過程是先創建集合在將待copy的集合的每個元素逐個加進去。所以在以后的使用中,我們應當盡量避免,在集合中添加可變對象。
未完待繼。。。