寫(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é)果和string
的copy
類(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.