IOS - 深入理解 深拷贝 与 浅拷贝

OC 数组中的深拷贝与浅拷贝

浅拷贝:即指针拷贝,源对象和新对象指向的是同一个地址,也就是说浅拷贝要复出出来一个新的文件,但两个文件的地址还是一个。浅拷贝的话是只有不可变数组(如:NSArray,NSSet,NS字典)遇上copy,才是浅拷贝,剩下的都是深拷贝。

Dog * dog1 = [Dog new];
// 这里就是浅拷贝,即指针拷贝
Dog * dog2 = dog1;

深拷贝,自己定义的类一般需要遵循 NSCopying, NSMutableCopying 协议

Dog.h

@interface Dog : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic) NSInteger age;
@end
Dog.m

@implementation Dog
- (id)copyWithZone:(NSZone *)zone
{
    Dog * dog = [[self class] allocWithZone:zone];
    dog.age = self.age;
    return dog;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    Dog * dog = [[self class] allocWithZone:zone];
    dog.age = self.age;
    return dog;
}

- (NSString *) description {
    return [NSString stringWithFormat:@"dog age: %ld", self.age];
}

@end
main.m

Dog * dog1 = [Dog new];
dog1.age = 5;

NSLog(@"dog1: %p, %@", dog1, dog1);

// mutableCopy 返回的是可变对象,自然可以设置对象的属性值
Dog * dog2 = [dog1 mutableCopy];
dog2.age = 10;

// copy 返回的对象也可以设置对象的属性值
// 所以,从这里来看,其实自定义实现的 copy 和 mutableCopy 功能是一致的,都是返回可变对象的
Dog * dog2 = [dog1 copy];
dog2.age = 10;

NSLog(@"dog2: %p, %@", dog2, dog2);


OC 可变数组中的深拷贝与浅拷贝

“数组中只是存储了对象的地址,而非存储了对象的本体。”

所以,对数组进行深拷贝之后,其中对象的地址确实被重新创建。

NSMutableArray * arr1 = [NSMutableArray new];

for (int i = 0; i < 3; i++) {
    Dog * dog = [Dog new];
    [arr1 addObject:dog];
}

NSLog(@"arr1: %p, %@", arr1, arr1);
NSMutableArray * arr2 = [arr1 mutableCopy];
NSLog(@"arr2: %p, %@", arr2, arr2);
运行结果:
2017-010-10 10:55:39.895 深拷贝与浅拷贝[901:303] arr1: 0x1001089c0, (
    "<Dog: 0x100108430>",
    "<Dog: 0x100109fc0>",
    "<Dog: 0x100109fd0>"
)
2017-10-10 10:55:39.917 深拷贝与浅拷贝[901:303] arr2: 0x100301ad0, (
    "<Dog: 0x100108430>",
    "<Dog: 0x100109fc0>",
    "<Dog: 0x100109fd0>"
)

因为 “数组中只是存储了对象的地址,而非存储了对象的本体。”所以,Dog 对象的空间并没有被复制。

因此,可变数组的 “深拷贝”并没有将其中所有元素都复制,其中的对象元素只进行了浅复制!

解决方法:重新将所有对象都拷贝一份

NSMutableArray * arr2 = [NSMutableArray new];
for (int i = 0; i < arr1.count; i++) {
    Dog * newDog = [arr1[i] copy];
    [arr2 addObject:newDog];
}

NSLog(@"arr2: %p, %@", arr2, arr2);

OC中对于数组的深拷贝,不能想当然地认为,已经将其中元素空间都拷贝了。实际上,其中的元素只是浅拷贝而已!

如果需要实现深拷贝的话,可以通过一个一个元素进行深拷贝,重新添加到可变数组中!


COPY 返回一个不可变对象的副本,MutalbeCopy返回一个可变对象的副本。

NSArray *array=[NSArray arrayWithObjects:@"one",@"two", nil];
NSMutableArray *array1=[array copy];
[array1 addObject:@"three"];  //error
NSMutableArray *array2=[array mutableCopy];
[array2 addObject:@"three"];  //right
// insert code here...
NSLog(@"Hello, World!");


比较与区别

复制对象的基本概念:复制一个对象为副本,开辟一块新的内存来存储副本对象。

如果一个对象想具备复制的功能,必须实现协议和协议

NSObject自带的常用的对象有:NSNumber、NSString、NSArray、NSDictionary、NSMutableArray、NSMutableDictionay、NSMutableString,copy产生的对象时不可变的,mutableCopy产生的对象时可变的

COPY和MutableCopy的区别

COPY 返回一个不可变对象的副本,MutalbeCopy返回一个可变对象的副本。


浅copy和深copy

浅复制尽复制对象本身,对象里的属性、包含的对象不做复制。

浅复制只是复制指针,并没有创建新的内存空间。

深复制复制全部,包括对象的属性和其他对象

Foundation框架支持复制的类,默认是浅复制


对象的自定义拷贝

对象拥有复制特性,必须实现NSCopying,NSMutableCopying协议,实现该协议的copyWithZone方法和mutableCopyWithZone方法

深拷贝和浅拷贝的区别就在于copyWithZone方法的实现,


copy、mutableCopy和retain之间的关系

在Foundation对象中,copy是一个不可变的对象时,作用相当于retain

当使用mutableCopy时,不管源对象是否可变,副本是可变的,并且实现真正意义上的copy

当我们使用copy一个可变对象时,副本对象是不可变的。


NSFoundation,当我们copy的时一个不可变对象时,默认的copy都是浅拷贝,相当于retain

当使用mutableCopy时,不管对象是否可变,都会实现深拷贝

retain相当于两个对象指向同一个指针


总结:copy与mutableCopy:
copy:对于可变对象为深拷贝,对于不可变对象是浅拷贝。返回对象是否可变与被复制的对象保持一致。
mutableCopy:始终是深拷贝。始终返回一个可变对象。




猜你喜欢

转载自blog.csdn.net/catstarxcode/article/details/79465640