iOS 深拷貝和淺拷貝

參考了一些文章,感覺寫的都不夠全面,於是決定自己進行實踐並整理,用來作爲對相關知識的總結。如有不合理之處,歡迎指正。

1、非容器型對象(例如 字符串)

屬性變量(通過修飾符)

創建model

@interface Fish : NSObject
@property (nonatomic,strong) NSString *strStrong;
@property (nonatomic,copy) NSString *strCopy;

@property (nonatomic,strong) NSMutableString *mutStrStrong;
@property (nonatomic,copy) NSMutableString *mutStrCopy;
@end
//可變字符串進行賦值
  NSMutableString *mutString = [NSMutableString stringWithFormat:@"1234"];

    Fish *f = [[Fish alloc] init];
    f.strStrong = mutString;
    f.strCopy = mutString;
    
    f.mutStrStrong = mutString;
    f.mutStrCopy = mutString;
    
    [mutString appendFormat:@"456"];
    
    NSLog(@"內容:%@-%@-%@",f.strStrong,f.strCopy,mutString);
    NSLog(@"Mut 內容:%@-%@-%@",f.mutStrStrong,f.mutStrCopy,mutString);
    
    NSLog(@"指針:%p-%p-%p",f.strStrong,f.strCopy,mutString);
    NSLog(@"Mut 指針:%p-%p-%p",f.mutStrStrong,f.mutStrCopy,mutString);

打印結果:

內容:1234456-1234-1234456
Mut 內容:1234456-1234-1234456

指針:0x1c4444a40-0xa000000343332314-0x1c4444a40
Mut 指針:0x1c4444a40-0xa000000343332314-0x1c4444a40

//總結:
//strong類型變量,不管是可變還是不可變,用可變字符串進行賦值,都是淺拷貝,指向同一個地址(指針拷貝),所以改變字符內容後,對應的內容也發生變化(copy 或 mutableCopy 則爲深拷貝)
//copy類型變量,賦值屬於深拷貝,指針地址發生變化(內容拷貝),所以改變字符串內容後,該變量不發生變化(copy 或 mutableCopy  都是深拷貝)


//由於 f.strCopy和 f.mutStrCopy指向同一個地址,
//猜想改變一個變量值另一個也會發生變化,
//於是進行以下操作:

//執行[f.mutStrCopy appendFormat:@"456"]  由於f.mutStrCopy 經過copy賦值後不可變,
//所以調用可變數組方法時就會出現Crash (同理修飾符copy 只能用來修飾不可變類型變量)

//f.mutStrCopy = @“456”直接賦值,則指針地址發生變化(同下不可變字符串賦值)
//所以此兩種情況都不會改變copy變量內容
//不可變字符串賦值
    NSString *str = @"123";
    
    Fish *f = [[Fish alloc] init];
    
    f.strStrong = str;
    f.strCopy = str;
    
    f.mutStrStrong = str;
    f.mutStrCopy = str;
    
    str = @"345";
        
    NSLog(@"內容:%@-%@-%@",f.strStrong,f.strCopy,str);
    NSLog(@"Mut 內容:%@-%@-%@",f.mutStrStrong,f.mutStrCopy,str);
    
    NSLog(@"指針:%p-%p-%p",f.strStrong,f.strCopy,str);
    NSLog(@"Mut 指針:%p-%p-%p",f.mutStrStrong,f.mutStrCopy,str);

打印結果

內容:123-123-345
Mut 內容:123-123-345
指針:0x104dbc2a0-0x104dbc2a0-0x104dbc2c0
Mut 指針:0x104dbc2a0-0x104dbc2a0-0x104dbc2c0

總結:
無論是否可變,不可變字符串賦值只是進行淺拷貝(指針拷貝),指向同一個地址
對源字符串進行重新賦值,則會指向新的指針地址,但是屬性指針還是原來的地址,所以已經賦值的變量不會發生變化

使用copy時,無論傳入的源數據是否可變,複製對象就是一個不可變的
使用strong修飾時,經過賦值後,該變量有可能會受到源數據的影響

2、容器型對象(例數組)

NSMutableArray *mutArr = [NSMutableArray arrayWithObjects:[NSMutableString stringWithFormat:@"1"],@"2",@"3", nil];

NSArray *arr = [mutArr copy];
    NSArray *mcArr = [mutArr mutableCopy]; //返回可變類型
    NSMutableArray *mutArr1 = [mutArr copy]; //返回不可變類型
    NSMutableArray *mutArr2 = [mutArr mutableCopy];
    
    NSMutableString *mutStr = mutArr.firstObject;

    [mutStr appendFormat:@"23"];
    
    
    NSLog(@"數據 : %@-%@-%@-%@-%@",mutArr,arr,mcArr,mutArr1,mutArr2);

    NSLog(@"數據地址 :%p-%p-%p-%p-%p",mutArr,arr,mcArr,mutArr1,mutArr2);    

[arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSLog(@"arr %p",obj);
    }];
    
    [mcArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSLog(@"mcArr %p",obj);
    }];
    
    [mutArr1 enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSLog(@"mutArr1 %p",obj);
    }];
    
    [mutArr2 enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSLog(@"mutArr2 %p",obj);
    }];
打印結果
數據 : (
    123,
    2,
    3
)-(
    123,
    2,
    3
)-(
    123,
    2,
    3
)-(
    123,
    2,
    3
)-(
    123,
    2,
    3
)
數據地址 :0x1c4454bb0-0x1c4454e80-0x1c4454b80-0x1c4454460-0x1c4454a00

arr 0x1c0452f30 arr 0x10072c3a0 arr 0x10072c3c0
mcArr 0x1c0452f30 mcArr 0x10072c3a0 mcArr 0x10072c3c0
mutArr1 0x1c0452f30 mutArr1 0x10072c3a0 mutArr1 0x10072c3c0
mutArr2 0x1c0452f30 mutArr2 0x10072c3a0 mutArr2 0x10072c3c0

總結:
源數據容器不可變,對不可變變量進行copy操作,只是進行對象的指針copy,該對象指向源容器指針地址,容器是淺拷貝;
源數據是可變類型,則無論被賦值對象是可變還是不可變,進行copy或者mutableCopy,生成不同的容器地址,容器是深拷貝;
容器內元素都指向同一塊地址,所以元素1發生改變,容器內元素也發生改變
容器內元素屬於淺拷貝(copy後返回的對象是不可變類型的,mutableCopy後返回的對象是可變類型)
//- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;

NSArray *items_copy = [[NSArray alloc] initWithArray:mutArr copyItems:YES];
NSMutableArray *mutitems_copy = [[NSMutableArray alloc] initWithArray:mutArr copyItems:YES];
調用該方法後,再次進行以上操作,打印結果:

數據 :...(
    1,
    2,
    3
)-(
    1,
    2,
    3
)

數據地址 :0x1c0440510-0x1c0440510-0x1c025f7d0-0x1c0440510-0x1c0440660-0x1c0444fe0-0x1c04405a0

mutArr2 0x1c0443bd0 mutArr2 0x1040c8420 mutArr2 0x1040c8440 mutArr2 0x1040c84a0
items_copy 0xa000000000000311 items_copy 0x1040c8420 items_copy 0x1040c8440
mutitems_copy 0xa000000000000311 mutitems_copy 0x1040c8420 mutitems_copy 0x1040c8440

總結:
調用該方法後,容器內元素地址發生改變,內容也沒有隨着源數據的變化而變化  所以容器和容器內元素都是深拷貝
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章