copy和mutableCopy
copy和mutableCopy之间的差异主要和深拷贝和浅拷贝有关,先看一下深拷贝、浅拷贝的概念。
深拷贝、浅拷贝
所谓浅拷贝,在Objective-C中可以理解为引用计数加1,并没有申请新的内存区域,只是另外一个指针指向了该区域。深拷贝正好相反,深拷贝会申请新的内存区域,原内存区域的引用计数不变。看图来说明深拷贝和浅拷贝的区别。
首先A指向一块内存区域,现在设置B = A
现在B和A指向了同一块内存区域,即为浅拷贝。
我们再来看看深考贝
首先A指向一块内存区域,现在设置B = A
A和B指向的不是同一块内存区域,只是这两块内存区域中的内容是一样的,即为深拷贝。
可变对象的copy、mutableCopy
- (void)testMutableCopy
{
NSMutableString *str1 = [NSMutableString stringWithString:@"abc"];
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"str1 = %p str2 = %p str3 = %p",str1,str2,str3);
NSMutableArray *array1 = [NSMutableArray arrayWithObjects:@"a",@"b", nil];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"array1 = %p array2 = %p array3 = %p",array1,array2,array3);
}
可变对象的copy和mutableCopy都是深拷贝,那么输出结果就是:
2019-02-01 13:01:27.525064+0800 TestClock[9357:143436] str1 = 0x60000086d8f0 str2 = 0xc8c1a5736a50d5fe str3 = 0x60000086d9b0
2019-02-01 13:01:27.525064+0800 TestClock[9357:143436] array1 = 0x600000868000 array2 = 0x60000067e5a0 array3 = 0x600000868030
我们可以看到,只要是可变对象,无论是集合对象,还是非集合对象,copy和mutableCopy都是深拷贝。
不可变对象的copy、mutableCopy
- (void)testCopy
{
NSString *str1 = @"123";
NSString *str2 = [str1 copy];
NSMutableString *str3 = [str1 mutableCopy];
NSLog(@"str1 = %p str2 = %p str3 = %p",str1,str2,str3);
NSArray *array1 = @[@"1",@"2"];
NSArray *array2 = [array1 copy];
NSMutableArray *array3 = [array1 mutableCopy];
NSLog(@"array1 = %p array2 = %p array3 = %p",array1,array2,array3);
}
不可变对象的copy是浅拷贝,mutableCopy是深拷贝,那么输出结果就是:
2019-02-01 13:01:27.525064+0800 TestClock[9442:147133] str1 = 0x1045612b0 str2 = 0x1045612b0 str3 = 0x6000017e4450
2019-02-01 13:01:27.525064+0800 TestClock[9442:147133] array1 = 0x6000019f5c80 array2 = 0x6000019f5c80 array3 = 0x6000017e1170
可以看到,只要是不可变对象,无论是集合对象,还是非集合对象,copy都是浅拷贝,mutableCopy都是深拷贝。
自定义对象如何支持copy方法
项目开发中经常会有自定义对象的需求,那么自定义对象是否可以copy呢?如何支持copy?
自定义对象可以支持copy方法,我们所需要做的是:自定义对象遵守NSCopying协议,且实现copyWithZone方法。NSCopying协议是系统提供的,直接使用即可。
- 遵守NSCopying协议:
@interface Student : NSObject <NSCopying>
{
NSString *_sex;
}
@property (atomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) int age;
@end
- 实现CopyWithZone方法:
- (instancetype)initWithName:(NSString *)name age:(int)age sex:(NSString *)sex
{
if(self = [super init]){
self.name = name;
_sex = sex;
self.age = age;
}
return self;
}
- (instancetype)copyWithZone:(NSZone *)zone
{
// 注意,copy的是自己,因此使用自己的属性
Student *stu = [[Student allocWithZone:zone] initWithName:self.name age:self.age sex:_sex];
return stu;
}
- 测试:
- (void)testStudent
{
Student *stu1 = [[Student alloc] initWithName:@"Wang" age:18 sex:@"male"];
Student *stu2 = [stu1 copy];
NSLog(@"stu1 = %p stu2 = %p",stu1,stu2);
}
- 结果:
stu1 = 0x600003a41e60 stu2 = 0x600003a41fc0
这里是一个深拷贝,根据copyWithZone方法的实现,应该很容易明白为何是深拷贝。
除了NSCopying协议和copyWithZone方法,对应的还有NSMutableCopying协议和mutableCopyWithZone方法,实现都是类似的,不做过多介绍。