編寫高質量ios代碼3

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的集合的每個元素逐個加進去。所以在以后的使用中,我們應當盡量避免,在集合中添加可變對象。

未完待繼。。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容