淺談iOS Copy與MutableCopy

500396052.jpg

寫了這么久的iOS,copy與mutableCopy也用了不少,可究竟什么時候用copy,什么時候用mutablecopy,他倆區(qū)別在哪,一直一知半解,也是論壇上的某些大神說這里用copy,那就用copy,比如NSString用copy修飾符。
不談從網上看到的深拷貝,淺拷貝概念,先從代碼看起,但開始代碼之前,還必須知道一個必須知道的概念:
要實現(xiàn)Copy 與MutableCopy必須要遵循Copy,MutableCopy協(xié)議
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding>如果沒有實現(xiàn)協(xié)議,調用copy,mutableCopy方法會Crash.

@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end

遵循NSCopying協(xié)議,就是實現(xiàn)copyWithZone方法,而關于copyWithZone
返回值id:Returns a new instance that’s a copy of the receiver.返回一個新實例
關于返回值的說明:

The returned object is implicitly retained by the sender, who is 
responsible for releasing it. The copy returned is immutable if the 
consideration “immutable vs. mutable” applies to the receiving 
object; otherwise the exact nature of the copy is determined by the 
class.

大意就是不管原始對象是不可變的或者是可變的,copy返回的都是不可變對象,copy之后的對象性質由原始對象確定。
同理,NSMutableCopying遵循協(xié)議

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

@end

返回值id:Returns a new instance that’s a mutable copy of the receiver.返回一個新實例
關于返回值說明

The returned object is implicitly retained by the sender, which is 
responsible for releasing it.
 The copy returned is mutable whether the original is mutable or not.

即不管原始對象是不是可變的,返回對象都是可變的。

NSString copy mutableCopy
    NSMutableString *string = [NSMutableString stringWithString:@"copy test"];
    NSString *origin = [NSString stringWithString:string];
    NSString *copy = [origin copy];
    NSMutableString *mut = [origin mutableCopy];
    
    NSLog(@"原始地址 %p,    %@",origin,origin);
    NSLog(@"copy地址 %p,    %@",copy,copy);
    NSLog(@"mut copy 地址 %p,    %@",mut,mut);
    [string appendString:@"aaa"];
    [mut appendString:@"ffff"];
    
    NSLog(@"原始地址 %p,    %@",origin,origin);
    NSLog(@"copy地址 %p,    %@",copy,copy);
    NSLog(@"mut copy 地址 %p,    %@",mut,mut);

輸出結果

原始地址 0x883205f046fa5f3f,    copy test
copy地址 0x883205f046fa5f3f,    copy test
mut copy 地址 0x6000015efab0,    copy test
原始地址 0x883205f046fa5f3f,    copy test
copy地址 0x883205f046fa5f3f,    copy test
mut copy 地址 0x6000015efab0,    copy testffff

我們分別對倆個可以改變值進行拼接修改,[NSString stringWithString:string]方法執(zhí)行的是開辟一個新的空間并拷貝string指向的值。
copy不管是原始字符串或copy后的字符串,都是不可變的,所以拷貝只需要將指針指向原始字符串的地址空間即可,即倆者是同一個玩意。
mutableCopy則需要開辟一個新的地址空間保存拷貝后的可變字符串所以地址必然變化,而由實驗可知,mut改變并不會影響原來的值,得知值也是重新拷貝了一份

結論
對于不可變字符串copy之后的其實就是本身,而mutableCopy會將整個對象拷貝一份

再來看NSMutableString

NSMutableString copy mutableCopy
    NSMutableString *string = [NSMutableString stringWithString:@"copy test"];
    
    NSMutableString *originString = [[NSMutableString alloc] initWithString:string];
    NSString *copystring = [originString copy];
    NSMutableString *mutystring = [originString mutableCopy];
    
    NSLog(@"原始地址 %p,    %@",originString,originString);
    NSLog(@"copy地址 %p,    %@",copystring,copystring);
    NSLog(@"mut copy地址 %p,    %@",mutystring,mutystring);
    
     [string appendString:@"xxxxxxx"];
    [originString appendString:originString];
    [mutystring appendString:@"0000000"];
    
    NSLog(@"修改后地址 %p,    %@",originString,originString);
    NSLog(@"修改后copy地址 %p,    %@",copystring,copystring);
    NSLog(@"修改后mutcopy地址 %p,    %@",mutystring,mutystring);

輸出結果

 原始地址 0x600002a55ef0,    copy test
 copy地址 0xf701af19378c9fa0,    copy test
 mut copy地址 0x600002a55bf0,    copy test
 修改后地址 0x600002a55ef0,    copy testcopy test
 修改后copy地址 0xf701af19378c9fa0,    copy test
 修改后mutcopy地址 0x600002a55bf0,    copy test0000000

由修改前輸出地址可知,對于可變字符串,不管是copy,mutableCopy都會開辟新的內存空間去保存拷貝后的地址
通過修改可變字符串之后可知,倆種拷貝都會對值進行拷貝

結論
對可變字符串,copy,mutableCopy 都會將整個對象重新拷貝

NSString為什么用推薦copy比如:
一個動物類,有一個屬性name,如果給name賦值的是不可變類型,皆大歡喜,copy與否都是一樣的,但是當賦值的是可變類型的話

NSMutableString *originString = [[NSMutableString alloc] initWithString:@"深拷貝,淺拷貝"];
self.name = originString;
[originString appendString:@"pengshuai"];

self.name會跟著變動,而copy不會有這種顧慮,而且不管name是可變,或不可變,使用copy不會相互影響。

看來看數(shù)組拷貝:

NSArray NSMutableArray
    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSArray *originArr = [NSArray arrayWithObject:array];
    NSArray *copyArr = [originArr copy];
    NSMutableArray *mutableArr = [originArr mutableCopy];
    
    NSLog(@"原始數(shù)組%p     %@",originArr,originArr);
    NSLog(@"Copy數(shù)組%p     %@",copyArr,copyArr);
    NSLog(@"MutableCopy數(shù)組%p     %@",mutableArr,mutableArr);
    
    [array addObject:@"juan"];
    [mutableArr addObject:@"xx00"];
    
    NSLog(@"修改數(shù)組%p     %@",originArr,originArr);
    NSLog(@"修改Copy數(shù)組%p     %@",copyArr,copyArr);
    NSLog(@"修改MutableCopy數(shù)組%p     %@",mutableArr,mutableArr);

輸出結果

原始數(shù)組0x6000001e8da0     (
    yao,
    peng
)
Copy數(shù)組0x6000001e8da0     (
    yao,
    peng
)
MutableCopy數(shù)組0x600000ff8330     (
    yao,
    peng
)
修改數(shù)組0x6000001e8da0     (
    yao,
    peng
)
修改Copy數(shù)組0x6000001e8da0     (
    yao,
    peng
)
修改MutableCopy數(shù)組0x600000ff8330     (
    yao,
    peng,
    xx00
)

得出的結果和stringcopy類型基本一樣

copy 兩者一樣,mutableCopy 對象拷貝一份
再看NSMutableArray

    NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSMutableArray *originArray = [NSMutableArray arrayWithArray:array];
    NSArray *copyArray = [originArray copy];
    NSMutableArray *mutableArray = [originArray mutableCopy];
    
    NSLog(@"原始數(shù)組%p     %@",originArray,originArray);
    NSLog(@"Copy數(shù)組%p     %@",copyArray,copyArray);
    NSLog(@"MutableCopy數(shù)組%p     %@",mutableArray,mutableArray);
    
    [array addObject:@"juan"];
    [originArray addObject:@"xxx"];
    [mutableArray addObject:@"000"];
    
    NSLog(@"修改數(shù)組%p     %@",originArray,originArray);
    NSLog(@"修改Copy數(shù)組%p     %@",copyArray,copyArray);
    NSLog(@"修改MutableCopy數(shù)組%p     %@",mutableArray,mutableArray);

輸出結果

原始數(shù)組0x600001ec8810     (
    yao,
    peng
)
Copy數(shù)組0x6000010daf40     (
    yao,
    peng
)
MutableCopy數(shù)組0x600001ec86f0     (
    yao,
    peng
)
修改數(shù)組0x600001ec8810     (
    yao,
    peng,
    xxx
)
修改Copy數(shù)組0x6000010daf40     (
    yao,
    peng
)
修改MutableCopy數(shù)組0x600001ec86f0     (
    yao,
    peng,
    000
)

對于可變數(shù)組,可以看到不管哪種copy,都會對對象重新拷貝,和可變字符串一樣啊,但是,數(shù)組容器有一個變數(shù),如下:

  NSMutableArray *array = [NSMutableArray arrayWithObjects:@"yao",@"peng", nil];
    NSMutableArray *originArray = [NSMutableArray arrayWithObject:array];
    NSArray *copyArray = [originArray copy];
    NSMutableArray *mutableArray = [originArray mutableCopy];
    
    NSLog(@"原始數(shù)組%p     %@",originArray,originArray);
    NSLog(@"Copy數(shù)組%p     %@",copyArray,copyArray);
    NSLog(@"MutableCopy數(shù)組%p     %@",mutableArray,mutableArray);
    
    [array addObject:@"juan"];
    [originArray addObject:@"xxx"];
    [mutableArray addObject:@"000"];
    
    NSLog(@"修改數(shù)組%p     %@",originArray,originArray);
    NSLog(@"修改Copy數(shù)組%p     %@",copyArray,copyArray);
    NSLog(@"修改MutableCopy數(shù)組%p     %@",mutableArray,mutableArray);

在看結果

原始數(shù)組0x60000177d530     (
        (
        yao,
        peng
    )
)
2019-01-03 16:15:08.055989+0800 ddd[16899:1986944] Copy數(shù)組0x600001b3c950     (
        (
        yao,
        peng
    )
)
2019-01-03 16:15:08.056074+0800 ddd[16899:1986944] MutableCopy數(shù)組0x60000177d1d0     (
        (
        yao,
        peng
    )
)
修改數(shù)組0x60000177d530     (
        (
        yao,
        peng,
        juan
    ),
    xxx
)
修改Copy數(shù)組0x600001b3c950     (
        (
        yao,
        peng,
        juan
    )
)
修改MutableCopy數(shù)組0x60000177d1d0     (
        (
        yao,
        peng,
        juan
    ),
    000
)

其他都類似,但是改變array的時候,不管哪種拷貝,數(shù)組也會跟著改變,是因為數(shù)組復制,其元素對象始終是指針復制,元素指向的值改變,數(shù)組自然都會改變。

由以上實驗大概得出以下結論

非集合對象 copy mutable copy
不可變對象 指針拷貝 指針拷貝,值拷貝
可變對象 指針拷貝,值拷貝 指針拷貝,值拷貝
集合對象 copy mutable copy
不可變對象 指針拷貝 指針拷貝,元素對象指針拷貝
可變對象 指針拷貝,元素對象指針拷貝 指針拷貝,元素對象指針拷貝
Block 與 Copy

使用block屬性的時候一般也使用copy,一般情況下,block默認保存在棧區(qū),在對外部對象進行操作時,不會對對象進行retain,而當block保存在堆區(qū)時,在外部對象進行操作時,會對對象進行retain。而我們本是是不知道什么時候什么時候調用block的,當block中的對象提前釋放,會造成Crash,使用copy關鍵字能保住block,防止調用的時候Crash.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.ios高性能編程 (1).內層 最小的內層平均值和峰值(2).耗電量 高效的算法和數(shù)據結構(3).初始化時...
    歐辰_OSR閱讀 30,193評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,621評論 1 32
  • 本文為轉載: 作者:zyydeveloper 鏈接:http://www.itdecent.cn/p/5f776a...
    Buddha_like閱讀 1,012評論 0 2
  • 前言 不敢說覆蓋OC中所有copy的知識點,但最起碼是目前最全的最新的一篇關于 copy的技術文檔了。后續(xù)發(fā)現(xiàn)有新...
    zyydeveloper閱讀 3,703評論 4 35
  • 月光下我的影子很瘦樹木的影子很長 很久沒看到影子了自行車,電動車,汽車藏起了影子我的夜晚如同白天 今夜我發(fā)現(xiàn)了夜像...
    伊甸隕石閱讀 146評論 0 0

友情鏈接更多精彩內容