淺談iOS Copy與MutableCopy

500396052.jpg

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

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

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

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.

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

@protocol NSMutableCopying

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

@end

返回值id:Returns a new instance that’s a mutable copy of the receiver.返回一個(gè)新實(shí)例
關(guān)于返回值說(shuō)明

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.

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

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);

輸出結(jié)果

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

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

結(jié)論
對(duì)于不可變字符串copy之后的其實(shí)就是本身,而mutableCopy會(huì)將整個(gè)對(duì)象拷貝一份

再來(lái)看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);

輸出結(jié)果

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

由修改前輸出地址可知,對(duì)于可變字符串,不管是copy,mutableCopy都會(huì)開(kāi)辟新的內(nèi)存空間去保存拷貝后的地址
通過(guò)修改可變字符串之后可知,倆種拷貝都會(huì)對(duì)值進(jìn)行拷貝

結(jié)論
對(duì)可變字符串,copy,mutableCopy 都會(huì)將整個(gè)對(duì)象重新拷貝

NSString為什么用推薦copy比如:
一個(gè)動(dòng)物類(lèi),有一個(gè)屬性name,如果給name賦值的是不可變類(lèi)型,皆大歡喜,copy與否都是一樣的,但是當(dāng)賦值的是可變類(lèi)型的話(huà)

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

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

看來(lái)看數(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);

輸出結(jié)果

原始數(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
)

得出的結(jié)果和stringcopy類(lèi)型基本一樣

copy 兩者一樣,mutableCopy 對(duì)象拷貝一份
再看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);

輸出結(jié)果

原始數(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
)

對(duì)于可變數(shù)組,可以看到不管哪種copy,都會(huì)對(duì)對(duì)象重新拷貝,和可變字符串一樣啊,但是,數(shù)組容器有一個(gè)變數(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);

在看結(jié)果

原始數(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
)

其他都類(lèi)似,但是改變array的時(shí)候,不管哪種拷貝,數(shù)組也會(huì)跟著改變,是因?yàn)閿?shù)組復(fù)制,其元素對(duì)象始終是指針復(fù)制,元素指向的值改變,數(shù)組自然都會(huì)改變。

由以上實(shí)驗(yàn)大概得出以下結(jié)論

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,547評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,135評(píng)論 1 32
  • 本文為轉(zhuǎn)載: 作者:zyydeveloper 鏈接:http://www.lxweimin.com/p/5f776a...
    Buddha_like閱讀 900評(píng)論 0 2
  • 前言 不敢說(shuō)覆蓋OC中所有copy的知識(shí)點(diǎn),但最起碼是目前最全的最新的一篇關(guān)于 copy的技術(shù)文檔了。后續(xù)發(fā)現(xiàn)有新...
    zyydeveloper閱讀 3,394評(píng)論 4 35
  • 月光下我的影子很瘦樹(shù)木的影子很長(zhǎng) 很久沒(méi)看到影子了自行車(chē),電動(dòng)車(chē),汽車(chē)藏起了影子我的夜晚如同白天 今夜我發(fā)現(xiàn)了夜像...
    伊甸隕石閱讀 125評(píng)論 0 0