今天來說一下iOS中的copy闷盔。
在iOS中,拷貝有兩種方式旅急,深拷貝(Deep copy)和淺拷貝(Shallow copy)逢勾。先上一張?zhí)O果官方的圖來表示一下這兩種拷貝。
可以看到藐吮,深拷貝是直接拷貝整個對象內(nèi)存到另一塊內(nèi)存中溺拱。淺拷貝則是僅僅拷貝一份指向?qū)ο蟮闹羔槪⒉豢截悓ο蟊旧怼?/p>
有大佬總結(jié)的一句話就是谣辞,深拷貝就像克隆你的人迫摔,你人沒有了,你的克隆人還在潦闲,而淺拷貝就像是克隆你的影子攒菠,你人如果沒有了,那你的影子也就沒有了歉闰。
但是上邊說的深拷貝辖众,其實也分成兩種,一種是單層深拷貝(one-level-deep copy)和敬,還有一種是完全深拷貝(true deep copy)凹炸。
蘋果官方有這樣一句話:
This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy, you can explicitly call for one as in Listing 2.
NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];
If you need a true deep copy, such as when you have an array of arrays, you can archive and then unarchive the collection, provided the contents all conform to the NSCoding protocol. An example of this technique is shown in Listing 3.
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
說白了,單層深拷貝和完全深拷貝從名字就能看出來昼弟,只有一層是深復(fù)制啤它,而另外一個是每一層都是深復(fù)制。
了解了深淺拷貝的概念舱痘,如何使用我們就不過多介紹了变骡,官方文檔里都有寫的很清楚,那接下來我們來討論一下開發(fā)中用到的各種類型的對象的拷貝吧芭逝。
首先我們先把我們平時用到的對象進(jìn)行一個分類塌碌,分成集合類型、非集合類型和自定義類型旬盯,其中集合類型就是NSArray台妆、NSDictionary這種翎猛,非集合類型就是NSString、NSNumber這種接剩。
這些類型也都有對應(yīng)的可變類型切厘,比如NSMutableArray、NSMutableDictionary懊缺、NSMutableString...
而拷貝也分為兩種疫稿,有mutableCopy和copy性含。
我們在集合類型中選出數(shù)組NSArray作為代表,非集合類型中選出NSString作為代表肝匆,來研究一下不同情況下的拷貝是深拷貝還是淺拷貝匹中,以及拷貝出來的對象是什么類型。排列組合一下就有如下幾種:
集合類型 + 可變類型 + mutableCopy
集合類型 + 可變類型 + copy
集合類型 + 不可變類型 + mutableCopy
集合類型 + 不可變類型 + copy
非集合類型 + 可變類型 + mutableCopy
非集合類型 + 可變類型 + copy
非集合類型 + 不可變類型 + mutableCopy
非集合類型 + 不可變類型 + copy
自定義類型 + mutableCopy
自定義類型 + copy
集合類型的拷貝
集合類型 + 可變類型
集合類型 + 可變類型 + mutableCopy
先上代碼:
/**
集合類型 - 可變類型 - mutableCopy
*/
- (void)setTypeMutableTypeMutableCopy
{
NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];
NSMutableArray *mutableCopyMutableArr = [mutableArr mutableCopy];
NSLog(@"%@",[[mutableArr mutableCopy] class]);
NSLog(@"%@ --- %p",mutableArr,mutableArr);
NSLog(@"%@ --- %p",mutableCopyMutableArr ,mutableCopyMutableArr);
}
打印結(jié)果:
2018-04-05 11:04:59.655093+0800 CopyDemo[11202:404553] __NSArrayM
2018-04-05 11:04:59.655338+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x60000024c060
2018-04-05 11:04:59.655475+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x60000024c5a0
可以看到結(jié)果傀顾,這種組合的情況下拷貝出的對象是一個可變數(shù)組(__NSArrayM),而兩個打印出的數(shù)組的地址不同,所以是深拷貝拣度。
集合類型 + 可變類型 + copy
先上代碼:
/**
集合類型 - 可變類型 - imutableCopy
*/
- (void)setTypeMutableTypeImutableCopy
{
NSMutableArray *mutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];
NSMutableArray *mutableCopyImutableArr = [mutableArr copy];
NSLog(@"%@",[[mutableArr copy] class]);
NSLog(@"%@ --- %p",mutableArr,mutableArr);
NSLog(@"%@ --- %p",mutableCopyImutableArr ,mutableCopyImutableArr);
}
打印結(jié)果:
2018-04-05 11:04:59.655633+0800 CopyDemo[11202:404553] __NSArrayI
2018-04-05 11:04:59.655754+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x604000250ef0
2018-04-05 11:04:59.655966+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x604000251400
可以看到結(jié)果拷貝出的是一個不可變數(shù)組(__NSArrayI),另外兩個數(shù)組的地址也不同螃壤,所以是深拷貝抗果。
集合類型 + 不可變類型
集合類型 + 不可變類型 + mutableCopy
先上代碼:
/**
集合類型 - 不可變類型 - mutableCopy
*/
- (void)setTypeImutableTypeMutableCopy
{
NSArray *imutableArr = [NSArray arrayWithObjects:@"first",@"second",@"third", nil];
NSArray *imutableArrMutableCopy = [imutableArr mutableCopy];
NSLog(@"%@",[[imutableArr mutableCopy] class]);
NSLog(@"%@ --- %p",imutableArr,imutableArr);
NSLog(@"%@ --- %p",imutableArrMutableCopy,imutableArrMutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.656128+0800 CopyDemo[11202:404553] __NSArrayM
2018-04-05 11:04:59.656245+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x604000251400
2018-04-05 11:04:59.656357+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x604000251190
可以看到,拷貝出來的是一個可變數(shù)組奸晴,而且兩個地址也不相同冤馏,為深拷貝。
集合類型 + 不可變類型 + copy
先上代碼:
/**
集合類型 - 不可變類型 - imutableCopy
*/
- (void)setTypeImutableTypeImutableCopy
{
NSArray *imutableArr = [NSArray arrayWithObjects:@"first",@"second",@"third", nil];
NSArray *imutableArrImutableCopy = [imutableArr copy];
NSLog(@"%@",[[imutableArr copy] class]);
NSLog(@"%@ --- %p",imutableArr,imutableArr);
NSLog(@"%@ --- %p",imutableArrImutableCopy,imutableArrImutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.656530+0800 CopyDemo[11202:404553] __NSArrayI
2018-04-05 11:04:59.656685+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x60000024c5a0
2018-04-05 11:04:59.656809+0800 CopyDemo[11202:404553] (
first,
second,
third
) --- 0x60000024c5a0
這個結(jié)果好像和之前的有點不同寄啼,兩個地址是相同的逮光,所以為淺拷貝,另外拷貝出來的是一個不可變數(shù)組墩划。
集合類型總結(jié)
| |可變類型|不可變類型
---|---|---
mutableCopy| 可變類型涕刚,深拷貝|可變類型,深拷貝 |
copy|不可變類型乙帮,深拷貝|不可變類型杜漠,淺拷貝|
非集合類型的拷貝
非集合類型中的拷貝出來的類型結(jié)果好像有點不對,這里暫時沒找出原因察净,如果有大佬看到希望能夠幫忙指正一下驾茴。
非集合類型 + 可變類型
非集合類型 + 可變類型 + mutableCopy
先上代碼:
#pragma mark -
#pragma mark - not set type
/**
非集合類型 - 可變類型 - mutableCopy
*/
- (void)unsetTypeMutableTypeMutableCopy
{
NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSMutableString *mutableStrMutableCopy = [mutableStr mutableCopy];
NSLog(@"%@",[[mutableStr mutableCopy] class]);
NSLog(@"%@ --- %p",mutableStr,mutableStr);
NSLog(@"%@ --- %p",mutableStrMutableCopy,mutableStrMutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.656967+0800 CopyDemo[11202:404553] __NSCFString
2018-04-05 11:04:59.657094+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251190
2018-04-05 11:04:59.657223+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251550
可以看到地址不同,深拷貝氢卡。
非集合類型 + 可變類型 + copy
先上代碼:
/**
非集合類型 - 可變類型 - imutableCopy
*/
- (void)unsetTypeMutableTypeImutableCopy
{
NSMutableString *mutableStr = [NSMutableString stringWithFormat:@"mutableStr"];
NSMutableString *mutableStrImutableCopy = [mutableStr copy];
NSLog(@"%@",[[mutableStr copy] class]);
NSLog(@"%@ --- %p",mutableStr,mutableStr);
NSLog(@"%@ --- %p",mutableStrImutableCopy,mutableStrImutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.661548+0800 CopyDemo[11202:404553] __NSCFString
2018-04-05 11:04:59.661682+0800 CopyDemo[11202:404553] mutableStr --- 0x604000251550
2018-04-05 11:04:59.661850+0800 CopyDemo[11202:404553] mutableStr --- 0x60400003c580
可以看到地址不同锈至,深拷貝。
非集合類型 + 不可變類型
非集合類型 + 不可變類型 + mutableCopy
代碼:
/**
非集合類型 - 不可變類型 - mutableCopy
*/
- (void)unsetTypeImutableTypeMutableCopy
{
NSString *str = [NSString stringWithFormat:@"ImutableStr"];
NSString *strMutableCopy = [str mutableCopy];
NSLog(@"%@",[[str mutableCopy] class]);
NSLog(@"%@ --- %p",str,str);
NSLog(@"%@ --- %p",strMutableCopy,strMutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.661968+0800 CopyDemo[11202:404553] __NSCFString
2018-04-05 11:04:59.662072+0800 CopyDemo[11202:404553] ImutableStr --- 0x60400003c580
2018-04-05 11:04:59.662184+0800 CopyDemo[11202:404553] ImutableStr --- 0x604000251340
同樣是深拷貝异吻,地址不同裹赴。
非集合類型 + 不可變類型 + copy
先上代碼:
/**
非集合類型 - 不可變類型 - imutableCopy
*/
- (void)unsetTypeImutableTypeImutableCopy
{
NSString *str = [NSString stringWithFormat:@"ImtableStr"];
NSString *strImutableCopy = [str copy];
NSLog(@"%@",[[str copy] class]);
NSLog(@"%@ --- %p",str,str);
NSLog(@"%@ --- %p",strImutableCopy,strImutableCopy);
}
打印結(jié)果:
2018-04-05 11:04:59.662703+0800 CopyDemo[11202:404553] __NSCFString
2018-04-05 11:04:59.663681+0800 CopyDemo[11202:404553] ImtableStr --- 0x600000237340
2018-04-05 11:04:59.663882+0800 CopyDemo[11202:404553] ImtableStr --- 0x600000237340
可以看到喜庞,地址是相同的,所以為淺拷貝棋返。
非集合類型總結(jié)
所以延都,可以發(fā)現(xiàn),集合與非集合類型的結(jié)果基本上相同睛竣,但是這里拷貝出的類型好像有點問題晰房。
| |可變類型|不可變類型
---|---|---
mutableCopy| 深拷貝|深拷貝 |
copy|深拷貝|淺拷貝|
自定義類型
我們先自定義一個model,里邊包括一個name射沟,一個location殊者。
注意這里要添加NSCopying和NSMutableCopying協(xié)議。
#import <Foundation/Foundation.h>
@interface CopyModel : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic ,copy) NSString *name;
@property (nonatomic ,copy) NSString *location;
@end
然后還需要實現(xiàn)兩個方法验夯,否則在copy或mutableCopy時會發(fā)生崩潰猖吴。
#import "CopyModel.h"
@implementation CopyModel
- (instancetype)copyWithZone:(NSZone *)zone
{
CopyModel *copyModel = [[CopyModel alloc] init];
copyModel.name = self.name;
copyModel.location = self.location;
return copyModel;
}
- (instancetype)mutableCopyWithZone:(NSZone *)zone
{
CopyModel *copyModel = [[CopyModel alloc] init];
copyModel.name = self.name;
copyModel.location = self.location;
return copyModel;
}
@end
接下來我們來看一下自定義類型的copy和mutableCopy的結(jié)果。
自定義類型 + mutableCopy
代碼:
/**
自定義類型 - mutableCopy
*/
- (void)customTypeMutableCopy
{
CopyModel *model = [[CopyModel alloc] init];
model.name = @"name";
model.location = @"location";
CopyModel *copyModel = [model mutableCopy];
NSLog(@"%@ --- %@ --- %p",model.name,model.location,model);
NSLog(@"%@ --- %@ --- %p",copyModel.name,copyModel.location,copyModel);
}
打印結(jié)果:
2018-04-05 11:04:59.664868+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c400
2018-04-05 11:04:59.665014+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c320
可以看到地址不同挥转,是深拷貝海蔽。
自定義類型 + copy
代碼:
/**
自定義類型 - ImutableCopy
*/
- (void)customTypeImutableCopy
{
CopyModel *model = [[CopyModel alloc] init];
model.name = @"name";
model.location = @"location";
CopyModel *copyModel = [model copy];
NSLog(@"%@ --- %@ --- %p",model.name,model.location,model);
NSLog(@"%@ --- %@ --- %p",copyModel.name,copyModel.location,copyModel);
}
打印結(jié)果:
2018-04-05 11:04:59.665165+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c400
2018-04-05 11:04:59.665404+0800 CopyDemo[11202:404553] name --- location --- 0x60400003c320
可以看到地址不同,是深拷貝绑谣。
@property 中的copy
屬性的常用修飾符里党窜,就有一個就是copy,那這個copy和其他的修飾符會有什么區(qū)別呢借宵?
copy vs strong
一般在修飾NSString屬性時幌衣,通常會用copy,那如果我們不用copy壤玫,而改用strong會發(fā)生什么問題呢豁护??
先上代碼:
@property (nonatomic ,strong) NSString *myStrongStr;
@property (nonatomic ,copy) NSString *myCopyStr;
- (void)strongStrVSCopyStr
{
NSMutableString *otherStr = [NSMutableString stringWithFormat:@"otherStr"];
self.myStrongStr = otherStr;
self.myCopyStr = otherStr;
[otherStr appendString:@"addSomeThing"];
NSLog(@"%@ --- %p",self.myStrongStr,self.myStrongStr);
NSLog(@"%@ --- %p",self.myCopyStr,self.myCopyStr);
NSLog(@"%@ --- %p",otherStr,otherStr);
}
打印結(jié)果:
2018-04-05 11:34:04.034151+0800 CopyDemo[11944:480031] otherStraddSomeThing --- 0x600000241170
2018-04-05 11:34:04.034339+0800 CopyDemo[11944:480031] otherStr --- 0xa000c45401541058
2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] otherStraddSomeThing --- 0x600000241170
結(jié)果應(yīng)該很明顯了垦细,我并沒有對myStrongStr進(jìn)行操作择镇,但是他的結(jié)果卻變了,而且他與otherStr的地址也是相同的括改,那這是為什么呢腻豌?
是因為copy修飾的屬性setter方法,調(diào)用時會先release舊值嘱能,copy新值再賦值給成員變量吝梅,不可變copy是深拷貝,地址就變化了惹骂。
而strong修飾之后只是強指針引用苏携,并沒有改變地址,所以myStrongStr會隨著otherStr的值進(jìn)行變化对粪。他們的地址也是相同的右冻。
copy vs retain
在MRC下進(jìn)行測試:
- (void)strongVSretain {
NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"111",@"222",@"333", nil];
NSMutableArray *arrMRetain = [arrM retain];
NSMutableArray *arrMCopy = [arrM copy];
[arrM removeLastObject];
NSLog(@"arrMCopy--%@--%p--%lu",arrMCopy,arrMCopy,[arrMCopy retainCount]);
NSLog(@"arrMRetain--%@--%p--%lu",arrMRetain,arrMRetain,[arrMRetain retainCount]);
}
打印結(jié)果:
2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] arrMCopy--(
111,
222,
333
)--0x60000005cf80--1
2018-04-05 11:34:04.034619+0800 CopyDemo[11944:480031] arrMRetain--(
111,
222
)--0x60000005cf50--2
可以看到装蓬,copy是深拷貝,retainCount為1纱扭,retain為淺拷貝牍帚,retain是使原來的引用計數(shù)+1,所以兩個數(shù)組時同一個地址乳蛾,改變的話也會一起改變暗赶。
可變類型使用copy修飾
當(dāng)我們創(chuàng)建一個可變類型的變量而使用copy修飾的時候,可能會發(fā)生一個問題肃叶,就是使用可變類型的方法時蹂随,就會發(fā)生崩潰。
比如:
@property (nonatomic ,copy) NSMutableArray *myMutableArr;
self.myMutableArr = [NSMutableArray arrayWithObjects:@"first",@"second",@"third", nil];
[self.myMutableArr addObject:@"forth"];
這段代碼運行時就會發(fā)生崩潰因惭,報錯信息為:
2018-04-05 11:42:52.239877+0800 CopyDemo[12135:500454] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x600000250200'
我們發(fā)現(xiàn)這個報錯的原因為不可變數(shù)組(__NSArrayI)找不到addObject:
的方法岳锁。那是為什么呢?
因為我們之前也說過筛欢,在使用copy修飾符的時候浸锨,是先release掉舊值,再copy出一個新的值版姑,而在前邊的內(nèi)容中也有說到,可變數(shù)組copy出來的是一個不可變數(shù)組迟郎,所以實際上這時候myMutableArr就是一個不可變數(shù)組了剥险,這時候給他發(fā)送可變數(shù)組的方法,他當(dāng)然就會崩潰了宪肖。
最后
最后總結(jié)一下表制,可以看到,只有在不可變類型的copy時控乾,才會發(fā)生淺拷貝么介,而其他所有的情況下都為深拷貝,并且一般來說無論是可變還是不可變類型的變量蜕衡,mutableCopy后都會變成可變類型壤短,copy后都會變成不可變類型。
最后Demo在這里慨仿。
祝大家清明節(jié)快樂~~