iOS 淺拷貝與深拷貝解析

1、什么是淺拷貝,什么是深拷貝

淺拷貝表示的是不拷貝內容,只拷貝對應的指針,即拷貝之后的值指向內存中的地址是一樣的。深拷貝表示的是不拷貝指針,而拷貝的是內容,即拷貝之后的值是不變的,但是指向內存中的地址和拷貝前對應的值的內存中的地址是不一樣的,可以說是全新的一個地址。

2、一個多層的數組進行拷貝,只有一層進行了深拷貝,這種算深拷貝?

這種不能說是深拷貝,只能說是單層深拷貝。

3、copy和mutableCopy

在使用copy和mutableCopy進行相關處理時需要區分集合對象和非集合對象。
集合對象:指的是NSArray,NSDictionary之類的
非集合對象:NSString,NSNumber之類的

  • 非集合對象中的imutable和mutable,imutable指的就是不可變對象,例如NSString,mutable指的是可變對象NSMutableString
    NSString *string = [NSString stringWithFormat:@"source"];
    NSString *cString = [string copy];
    NSMutableString *mString = [string mutableCopy];
    NSLog(@"%p",string);//0xa00656372756f736
    NSLog(@"%p",cString);//0xa00656372756f736
    NSLog(@"%p",mString);//0x60c00024f180

    NSMutableString *mutableString = [NSMutableString stringWithFormat:@"source"];
    NSString *cString = [mutableString copy];
    NSString *mString = [mutableString mutableCopy];
    NSLog(@"%p",mutableString);//0x600000245f40
    NSLog(@"%p",cString);//0xa00656372756f736
    NSLog(@"%p",mString);//0x600000246060

從最后輸出的地址可以看出,在非集合對象中,當對象為imutable時,copy是淺拷貝,mutableCopy是深拷貝,當對象為mutable時,copy和mutableCopy都是深拷貝。還有一個很有意思需要注意的地方:執行下面代碼時會出現什么問題?

    NSMutableString *mString1 = [mutableString copy];
    [mString1 appendString:@"source"];

答案就是程序會crash,原因就是非集合類型中的mutable的對象使用copy返回的是imutable的值,雖然是深拷貝,但是類型變了,調用了不存在的方法,自然就會crash。

  • 集合對象
    也分imutable和mutable對象
    NSArray *array = @[@"1",@"2",@"3"];
    NSArray *cArray = [array copy];
    NSMutableArray *mArray = [array mutableCopy];
    NSLog(@"%p",array);//0x608000243930
    NSLog(@"%p",cArray);//0x608000243930
    NSLog(@"%p",mArray);//0x608000243cc0

    NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:@"1",@"2",@"3", nil];
    NSArray *cArray = [mutableArray copy];
    NSMutableArray *mArray = [mutableArray mutableCopy];
    NSLog(@"%p",mutableArray);//0x60000024ff60
    NSLog(@"%p",cArray);//0x600000250650
    NSLog(@"%p",mArray);//0x600000250530

從上面輸出的內存地址可以看出,不可變的集合對象使用copy是淺拷貝,使用mutableCopy是深拷貝。可變的集合對象使用copy和mutableCopy都是深拷貝。同樣的問題,如果

    NSMutableArray *mArray1 = [mutableArray copy];
    [mArray1 addObject:@"4"];

同樣是會導致crash。原因和上面所說的一樣。這也是為什么可變類型的對象不要使用copy進行修飾的原理。
最后結論 :不管是集合對象和非集合對象,其不可變對象,使用copy是淺拷貝,只會拷貝指針,而mutableCopy是深拷貝。對于可變對象,使用copy是深拷貝,但是深拷貝之后的值imutable類型的,不能使用mutable類型才有的方法。使用mutableCopy是深拷貝。 當一個NSArray實例用strong修飾而不是用copy修飾,那么NSArray的賦值是一個可變數組的時候,只是對可變對象進行了淺拷貝,當可變數組改變的時候,NSArray的實例也會發生改變,這會導致一些bug,如果用copy修飾,那么就會對可變數組進行深拷貝,得到一個新的不可變數組,自然不會因為可變數組的改變而導致當前數組的改變。

4、多層拷貝
    Company *c1 = [[Company alloc] init];
    c1.name = @"c1";
    c1.location = @"HK";
    
    Company *c2 = [[Company alloc] init];
    c2.name = @"c2";
    c2.location = @"USA";
    
    People *p1 = [[People alloc] init];
    p1.name = @"Jim";
    [p1.companyInfo addObject:c1];
    [p1.companyInfo addObject:c2];
    
    People *p2 = [[People alloc] init];
    p2.name = @"KX";
    [p2.companyInfo addObject:c1];
    [p2.companyInfo addObject:c2];
    
    NSMutableArray *data = [NSMutableArray arrayWithObjects:p1,p2, nil];
    NSLog(@"1:");
    int i =0;
    NSLog(@"data:%p",data);
    for (People *p in data) {
        if (i==0) {
            NSLog(@"p1");
        }else{
            NSLog(@"p2");
        }
        NSLog(@"%p",p);
        NSLog(@"%p",p.name);
        i++;
    }
    i = 0;
    NSLog(@"2:");
    NSArray *cArray = [data copy];
    NSLog(@"cArray:%p",cArray);
    for (People *p in cArray) {
        if (i==0) {
            NSLog(@"p1");
        }else{
            NSLog(@"p2");
        }
        NSLog(@"%p",p);
        NSLog(@"%p",p.name);
         i++;
    }
    NSLog(@"3:");
    i = 0;
    NSMutableArray *mArray = [data mutableCopy];
    NSLog(@"mArray:%p",mArray);
    for (People *p in mArray) {
        if (i==0) {
            NSLog(@"p1");
        }else{
            NSLog(@"p2");
        }
        NSLog(@"%p",p);
        NSLog(@"%p",p.name);
         i++;
    }

log:

1:
data:0x6040002528a0
p1
0x6040000368e0
0x1055ce118
p2
0x604000036940
0x1055ce138
2:
cArray:0x60c0000339e0
p1
0x6040000368e0
0x1055ce118
p2
0x604000036940
0x1055ce138
3:
mArray:0x600000054880
p1
0x6040000368e0
0x1055ce118
p2
0x604000036940
0x1055ce138

從輸出的地址可以看出,雖然copy和mutableCopy輸出的地址都是不一樣的,也屬于深拷貝,但是里面的對象的地址都是一樣的,這說明這樣拷貝只是上面所說的單層深拷貝。而不是完整深拷貝。
解決方案:
新建一個數組,然后把數組中的數據取出來進行拷貝,再把數據放進新數組中。這樣就能實現數組中的數據深拷貝。
//未完待續,還有NSCopying和NSMutableCopying以及實現copywithZone的注意點。

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

推薦閱讀更多精彩內容