
寫了這么久的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
)
得出的結果和string的copy類型基本一樣
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.