淺拷貝和深拷貝
蘋(píng)果官方文檔是這樣解釋的
There are two kinds of object copying: shallow copies and deep copies. The normal copy is a shallow copy that produces a new collection that shares ownership of the objects with the original. Deep copies create new objects from the originals and add those to the new collection.
谷歌翻譯
有兩種對(duì)象復(fù)制:淺拷貝和深拷貝。 正常副本是一個(gè)淺拷貝,它產(chǎn)生一個(gè)新集合从绘,它與原始對(duì)象共享對(duì)象的所有權(quán)乾蓬。 深度副本從原始文件創(chuàng)建新對(duì)象篡诽,并將它們添加到新集合厂抖。
更直觀的解釋如下圖:
NSArray的copy
NSArray內(nèi)部已經(jīng)實(shí)現(xiàn)了NSCopying協(xié)議并蝗,所以可以直接調(diào)用copy方法蝠猬,假如其內(nèi)部沒(méi)實(shí)現(xiàn)的話就需要我們自己實(shí)現(xiàn)- (id)copyWithZone:(NSZone *)zone
方法切蟋,否則就會(huì)報(bào)錯(cuò)。NSMutableArray同理榆芦。
而Foundation框架中提供的所有集合默認(rèn)都是淺拷貝柄粹,驗(yàn)證如下
新建Student和Name類,代碼挺簡(jiǎn)單匆绣,就不做解釋了
@interface Name : NSObject<NSCopying>
@property (nonatomic, copy) NSString *surname; // 姓
@property (nonatomic, copy) NSString *firstName; // 名
@end
@implementation Name
- (id)copyWithZone:(NSZone *)zone{
Name *copy = [[[self class] allocWithZone:zone] init];
return copy;
}
@end
@interface Student : NSObject<NSCopying>
@property (nonatomic, strong) Name *name;
@property (nonatomic, strong) NSString *address;
@end
@implementation Student
- (instancetype)init {
self = [super init];
if (self) {
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
Student *copy = [[[self class] allocWithZone:zone] init];
copy.name = [self.name copy];
return copy;
}
@end
然后驻右,構(gòu)建NSArray,通過(guò)mutableCopy拷貝一份并打印拷貝前后兩者的地址進(jìn)行比較犬绒,代碼如下
Student *student = [Student new];
Name *name = [Name new];
student.name = name;
NSMutableArray *studentsArray = [NSMutableArray new];
[studentsArray addObject:student];
NSMutableArray *studentsArrayCopy = [studentsArray mutableCopy];
NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);
打印結(jié)果
studentsArray[0]:0x1002013b0
studentsArrayCopy[0]:0x1002013b0
studentsArrayAddress:0x100203210
studentsArrayCopyAddress:0x100203320
可見(jiàn)studentsArray(原數(shù)據(jù))和studentsArrayCopy(拷貝后的數(shù)據(jù))地址是不同的旺入,而數(shù)組中的元素的地址相同,可見(jiàn)通過(guò)mutableCopy方法實(shí)現(xiàn)的拷貝是淺拷貝凯力。
數(shù)組還提供了一種copy方法茵瘾,- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
,flag表示是否把數(shù)組元素也拷貝一份,如果數(shù)組的元素沒(méi)有實(shí)現(xiàn)NSCopying協(xié)議而且flag設(shè)置成YES的話咐鹤,調(diào)用該方法是會(huì)Crash的拗秘。如果數(shù)組中的所有層級(jí)都實(shí)現(xiàn)了NSCopying方法的話,這種方式的拷貝就是所謂的深拷貝祈惶,即完全復(fù)制一份雕旨。驗(yàn)證如下
Student *student = [Student new];
Name *name = [Name new];
student.name = name;
NSMutableArray *studentsArray = [NSMutableArray new];
[studentsArray addObject:student];
NSMutableArray *studentsArrayCopy = [[NSMutableArray alloc] initWithArray:studentsArray copyItems:YES];
NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);
Name *nameCopy = ((Student *)studentsArrayCopy[0]).name;
NSLog(@"\nname:%p",name);
NSLog(@"\nnameCopy:%p",nameCopy);
打印結(jié)果如下
studentsArray[0]:0x100300070
studentsArrayCopy[0]:0x100300930
studentsArrayAddress:0x100300700
studentsArrayCopyAddress:0x100300a00
name:0x100300130
nameCopy:0x1003009e0
所有的原對(duì)象和copy對(duì)象都不同,可見(jiàn)是深拷貝捧请。當(dāng)我們要實(shí)現(xiàn)復(fù)制一個(gè)數(shù)組并在操作之后對(duì)原數(shù)組無(wú)影響的需求時(shí)就可以通過(guò)這種方式凡涩。
當(dāng)然,如果數(shù)組層次很多的話疹蛉,這方式明顯不可取活箕,幸運(yùn)的是蘋(píng)果提供了一種更簡(jiǎn)單的實(shí)現(xiàn)方式(本人沒(méi)驗(yàn)證過(guò))
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
還有,- (instancetype)initWithArray:(NSArray<ObjectType> *)array;
這種拷貝方式和- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
方式是一樣的可款,當(dāng)后者的flag設(shè)置成NO的時(shí)候育韩,這兩個(gè)這兩方法是完全相同的克蚂。而且這樣方式和直接調(diào)用copy方式的拷貝也是一樣的
Student *student = [Student new];
Name *name = [Name new];
student.name = name;
NSMutableArray *studentsArray = [NSMutableArray new];
[studentsArray addObject:student];
NSMutableArray *studentsArrayCopy = [[NSMutableArray alloc] initWithArray:studentsArray copyItems:NO];
NSLog(@"\nstudentsArray[0]:%p",studentsArray[0]);
NSLog(@"\nstudentsArrayCopy[0]:%p",studentsArrayCopy[0]);
NSLog(@"\nstudentsArrayAddress:%p",studentsArray);
NSLog(@"\nstudentsArrayCopyAddress:%p",studentsArrayCopy);
Name *nameCopy = ((Student *)studentsArrayCopy[0]).name;
NSLog(@"\nname:%p",name);
NSLog(@"\nnameCopy:%p",nameCopy);
打印結(jié)果
studentsArray[0]:0x1004041d0
studentsArrayCopy[0]:0x1004041d0
studentsArrayAddress:0x1004060c0
studentsArrayCopyAddress:0x100406260
name:0x100405ef0
nameCopy:0x100405ef0
數(shù)組地址不同,內(nèi)部元素相同筋讨。沒(méi)毛病埃叭。
總結(jié)
- 淺拷貝:
1, 直接調(diào)用copy/mutableCopy方法
2,[[NSMutableArray alloc] initWithArray:studentsArray copyItems:NO]
3,[[NSMutableArray alloc] initWithArray:studentsArray]
- 深拷貝:
1,[[NSMutableArray alloc] initWithArray:studentsArray copyItems:YES]
,前提要自己實(shí)現(xiàn)每一層級(jí)的NSCopying協(xié)議
2,[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]]