深拷贝与浅拷贝
深拷贝与浅拷贝
- 深拷贝与浅拷贝
- 定义
- 按照类型说明
- 非容器类对象的深拷贝与浅拷贝
- 不可变字符串
- 可变类型字符串
- 容器类对象的深浅拷贝
- 自定义类对象的深浅拷贝
- 容器类对象的完全深拷贝
- 1.copyItems
- 2.解档和归档
定义
深拷贝:简单来说就是创建一个与被复制对象的值完全相同的对象,但是他们的地址不同。
浅拷贝:浅拷贝就是仅仅创建一个存放复制对象相同地址的指针变量
按照类型说明
非容器类对象的深拷贝与浅拷贝
这里我们从字符串来作为例子。
不可变字符串
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSString* str1 = @"dddddd";
NSString* str2 = [str1 copy];
NSString* str3 = [str1 mutableCopy];
NSMutableString* str4 = [str1 copy];
NSMutableString* str5 = [str1 mutableCopy];
NSLog(@"str1:%p", str1);
NSLog(@"str2:%p", str2);
NSLog(@"str3:%p", str3);
NSLog(@"str4:%p", str4);
NSLog(@"str5:%p", str5);
}
return 0;
}
下面的打印结果为:
我们可以发现一个事情就是str1和str2和str4三者的地址是相同的。所以可以得出一个结论:就是如果是字符串常量的话,只要是copy就是浅拷贝,mutableCopy就是深拷贝。tips:我们用[NSString stringWithstring:]方式创建的也是一个常量区字符串。
可变类型字符串
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString* str1 = [NSMutableString stringWithString:@"helloworld"];
NSMutableString* str2 = [str1 copy];
NSMutableString* str3 = [str1 mutableCopy];
NSString* str4 = [str1 copy];
NSString* str5 = [str1 mutableCopy];
NSLog(@"str1:%p", str1);
NSLog(@"str2:%p", str2);
NSLog(@"str3:%p", str3);
NSLog(@"str4:%p", str4);
NSLog(@"str5:%p", str5);
}
return 0;
}
打印结果如下:
这里我们发现这里的两种拷贝都是深拷贝,它都重新创建了一块新的区域存储这部分内容。
因此可以得出一个结论:
- 可变对象copy后的对象是不可变的,mutableCopy后的对象是可变的。
- 对于可变对像的复制都是深拷贝,
复制后的对象类型仅仅和我们的对象的选择的复制方式有关。
容器类对象的深浅拷贝
NSArray:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray* ary1 = [NSArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
NSArray* ary2 = [ary1 copy];
NSArray* ary3 = [ary1 mutableCopy];
NSMutableArray* ary4 = [ary1 copy];
NSMutableArray* ary5 = [ary1 mutableCopy];
NSLog(@"arry1:%p", ary1);
NSLog(@"arry2:%p", ary2);
NSLog(@"arry3:%p", ary3);
NSLog(@"arry4:%p", ary4);
NSLog(@"arry5:%p", ary5);
}
return 0;
}
打印结果如下:
NSMutableArray:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray* ary1 = [NSMutableArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
NSArray* ary2 = [ary1 copy];
NSArray* ary3 = [ary1 mutableCopy];
NSMutableArray* ary4 = [ary1 copy];
NSMutableArray* ary5 = [ary1 mutableCopy];
NSLog(@"arry1:%p", ary1);
NSLog(@"arry2:%p", ary2);
NSLog(@"arry3:%p", ary3);
NSLog(@"arry4:%p", ary4);
NSLog(@"arry5:%p", ary5);
}
return 0;
}
打印结果如下:
这里可以发现其实两者和字符串类型类似,也是遵循可变的copy和mutableCopy都是深拷贝,而不可变类型则copy是浅拷贝而mutableCopy是深拷贝。
但是这里我们还没有考虑到有关容器内部元素的深浅拷贝的一个情况。
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray* ary1 = [NSMutableArray arrayWithObjects:@"krystal", @"xiyang", @"zzr", nil];
NSArray* ary2 = [ary1 copy];
NSArray* ary3 = [ary1 mutableCopy];
NSMutableArray* ary4 = [ary1 copy];
NSMutableArray* ary5 = [ary1 mutableCopy];
NSLog(@"arry1:%p,ary1[0]:%p,ary1[1]:%p,ary1[2]:%p", ary1, ary1[0], ary1[1], ary1[2]);
NSLog(@"arry2:%p,ary2[0]:%p,ary2[1]:%p,ary2[2]:%p", ary2, ary2[0], ary2[1], ary2[2]);
NSLog(@"arry3:%p,ary3[0]:%p,ary3[1]:%p,ary3[2]:%p", ary3, ary3[0], ary3[1], ary3[2]);
NSLog(@"arry4:%p,ary4[0]:%p,ary4[1]:%p,ary4[2]:%p", ary4, ary4[0], ary4[1], ary4[2]);
NSLog(@"arry5:%p,ary5[0]:%p,ary5[1]:%p,ary5[2]:%p", ary5, ary5[0], ary5[1], ary5[2]);
}
return 0;
}
这里不难发现尽管我们是对于每一个容器进行了一个深拷贝,但是不难发现我们的所有元素仅仅只是被浅拷贝了,这里我们可以将它称为数组中元素的浅层的深拷贝。
Tip:我们数组中的元素实际上是对象的引用,所以我们访问数组的第一个元素的时候,不管是从原始数组还是从拷贝的不可变数组抑或是拷贝的可变数组中访问,都会得到同一个字符串对象的引用。
这句话的意思是,在数组中存储的元素实际上是指向对象的引用,而不是对象本身。当我们将一个对象添加到数组中时,实际上是将该对象的引用存储在数组的对应位置上。
自定义类对象的深浅拷贝
自定义类对象的拷贝需要我们的类别遵循NSCopying协议,
接口部分
容器类对象的完全深拷贝
1.copyItems
我们先来给出它的定义:
copyItems: 方法是 NSArray 和 NSMutableArray 的一个方法,用于创建一个新的数组,并对数组中的元素进行拷贝。该方法会遍历原始数组的每个元素,并对每个元素执行copy操作,然后将拷贝后的元素添加到新的数组中。
可以看到定义中的关键是遍历数组中的每个元素对其进行copy
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString* str1;
FKUser* p1 = [[FKUser alloc] initWithName:@"krystal" pass:@"12"];
FKUser* p2 = [p1 copy];
NSArray* ary = [NSArray arrayWithObjects:p1, p2, nil];
NSArray* ary1 = [[NSArray alloc] initWithArray:ary copyItems:YES];
NSLog(@"%p, %p", ary, ary1);
NSLog(@"%p, %p", ary1[0], ary[0]);
FKUser* p3 = ary1[0];
FKUser* p4 = ary[0];
NSLog(@"%p, %p", p3.name, p4.name);
}
return 0;
}
打印结果为:
这个展示出我们对于数组内部的元素也进行一了一次深拷贝。这个方法实现了我们的对于容器内部的一个深拷贝。但是,要注意一个点,就是我们容器类对象中的元素是非容器类才可以实现一个复制,但是注意这里我们没有对对象进行更深一层的拷贝,如果我们想实现一个更深一次层次的拷贝则需要进行一递归拷贝的方式。
即:
- (id)copyWithZone:(NSZone*)zone {
NSLog(@"对象复制");
FKUser* newUser = [[[self class] allocWithZone:zone] init];
newUser.name = [self.name mutableCopy];
newUser.pass = [self.pass copy];
return newUser;
}
修改了这部分代码后就可以得到下面的一个结果:
但是,要注意一个点,就是我们容器类对象中的元素是非容器类才可以实现一个复制,下面是后有关拷贝容器的内容
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableString* str1;
FKUser* p1 = [[FKUser alloc] initWithName:@"krystal" pass:@"12"];
FKUser* p2 = [p1 copy];
NSArray* ary = [NSArray arrayWithObjects:p1, p2, nil];
NSArray* ary1 = [NSArray arrayWithObject:ary];
NSArray* ary2 = [[NSArray alloc] initWithArray:ary1 copyItems:YES];
NSLog(@"%p, %p",ary1, ary2);
NSLog(@"%p, %p, %p",ary1[0], ary2[0], ary);
}
return 0;
}
这里我们就会发现这里的对于容器的深拷贝过程失败了,我们这里还是仅仅实现了一次浅拷贝。
因此我们可以得出一个结论:该方法进行深拷贝仅仅只适用于数组的直接元素,不能对于数组中的容器对象进行一个拷贝。
2.解档和归档
我们在iOS中间可以通过解档和归档的方式实现一个完全意义上的一个完全拷贝,笔者也不是很理解相关的内容,只能简单给出对应的代码:
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:ary]];
以上就是有关深拷贝和浅拷贝的内容