好久没有写博客了,今天得空写一写
前几天,小伙伴微信发过来一段代码,想要对model进行深拷贝,代码如下:
(Person类中没有任何代码)
Person *person1 = [[Person alloc] init];
Person *person2 = [person1 mutableCopy];
但是这样会崩溃的,具体原因主要与深拷贝只是相关
如果想要这样的拷贝不崩溃,只能进行拷贝构造了
在Person.h中遵循 <NSMutableCopying>协议,
在Person.m中实现
- (id)mutableCopyWithZone:(NSZone *)zone{
Person *people = [[self class] allocWithZone:zone];
return people;
}
这样可以进行深拷贝,那么下面来说说我和小伙伴对深拷贝的一些浅谈吧.
1. copy 和retain
copy 是创建一个新的对象,retain count 为1,开辟一个新的内存,与旧的对象无关. 但属性标识对象内容相同. retain是创建一个指针,引用对象计数+1 . copy 减少对象了对上下文的依赖.
2. copy 和 strong
copy和strong 都是对对象的强引用, 区别和上文retain区别差不多
3. copy 和 mutableCopy
ios 提供copy 和 mutableCopy 两个方法. copy 复制可到的是一个不可变的对象(这一点很重要),因此copy得来的对象,不能进行可变对象方法的操作, mutableCopy复制的对象得到的是一个可变的对象,因此必须要用可变对象来接受复制的对象
下面结合例子来进行理解:
首先先宏定义打印
#define LOG_METHOD_NAME NSLog(@"%@",NSStringFromSelector(_cmd));
#define LOG_OBJ_ADDRESS(obj) NSLog(@"%@ : %p",NSStringFromClass([obj class]),obj);
(一) 常用不可变字符串赋值思考
NSString *str = @"222";
LOG_OBJ_ADDRESS(str)………(1)
NSString *str2 = str;
LOG_OBJ_ADDRESS(str2)………(2)
str2 = @“222”;
LOG_OBJ_ADDRESS(str2)…………(3)
NSString *str3 = [str copy];
LOG_OBJ_ADDRESS(str3)…………(4)
NSString *str3 = @“222”;
LOG_OBJ_ADDRESS(str3)…………(5)
NSString *str4 = [NSString stringWithFormat:@"%@",@"444"];
LOG_OBJ_ADDRESS(str4)…………(6)
str2 = @“dddd”;
LOG_OBJ_ADDRESS(str2)…………(7)
str3 = @“aaaa”;
LOG_OBJ_ADDRESS(str3)…………(8)
打印的结果是:
_NSCFConstantString : 0x107f85260 (1)
_NSCFConstantString : 0x107f85260 (2)
_NSCFConstantString : 0x107f85260 (3)
_NSCFConstantString : 0x107f85260 (4)
_NSCFConstantString : 0x107f85260 (5)
NSTaggedPointerString : 0xa000000003434343 (6)
_NSCFConstantString : 0x107f852a0 (7)
_NSCFConstantString : 0x107f852b0 (8)
(1)(2)(3)(4)(5) 内存地址相同,说明无论在直接赋值还是copy,都是将两个变量都是指向同一个内存地址. 而且,,在赋的值相同情况下,也是指向同一个内存地址, 但是在赋值不一样的情况下,如 (7) (8) 会开辟出一个新的内存地址,相当于深拷贝
问题一: 那么问题来了,既然这样赋值就能实现字符串的深拷贝,减少了两个字符串之间影响,那么copy有什么用呢???
(二) 关于copy 在定义属性时的作用
(1) 不可变字符串
Person.h
@property (nonatomic, strong) NSString *stt_strong;
@property (nonatomic, copy) NSString * stt_copy;
Person.m
NSString *stt = @"字符串";
LOG_OBJ_ADDRESS(stt)
self.stt_strong = stt;
LOG_OBJ_ADDRESS(self.stt_strong)
self.stt_copy = stt;
LOG_OBJ_ADDRESS(self.stt_copy)
stt = @"字符串改变了";
LOG_OBJ_ADDRESS(self.stt_strong)
LOG_OBJ_ADDRESS(stt)
LOG_OBJ_ADDRESS(self.stt_copy)
打印结果:
__NSCFConstantString : 0x10f36e2c0 字符串
__NSCFConstantString : 0x10f36e2c0 字符串
__NSCFConstantString : 0x10f36e2c0 字符串
__NSCFConstantString : 0x10f36e2c0 字符串
__NSCFConstantString : 0x10f36e2e0 字符串改变了
__NSCFConstantString : 0x10f36e2c0 字符串
结论:在定义不可变字符串时,属性定义用copy 和 strong 中任意一种修饰,其效果是一样的,
原有字符串值改变,对全局属性没有任何影响,这相当于在同一个内存块儿内,有不同的区域进行存储字符串.
当全局变量发生改变的时候,会重新开辟内存空间,相当于深拷贝,进行存储
(2)可变字符串
Person.h
@property (nonatomic, strong) NSMutableString *mutStr_strong;
@property (nonatomic, copy) NSMutableString *mutStr_copy;
Person.m
NSMutableString *mutS = [NSMutableString stringWithFormat:@"rrrrrr"];
LOG_OBJ_ADDRESS(mutS) ………………………(1)
self.mutStr_strong = mutS;
LOG_OBJ_ADDRESS(self.mutStr_strong); ………………………(2)
self.mutStr_copy = mutS;
LOG_OBJ_ADDRESS(self.mutStr_copy) ………………………(3)
[mutS appendFormat:@"tttttttt"];
LOG_OBJ_ADDRESS(mutS); ………………………(4)
LOG_OBJ_ADDRESS(self.mutStr_strong) ………………………(5)
LOG_OBJ_ADDRESS(self.mutStr_copy) ………………………(6)
打印结果:
__NSCFString : 0x600000073640 rrrrrr ………………………(1)
__NSCFString : 0x600000073640 rrrrrr ………………………(2)
NSTaggedPointerString : 0xa007272727272726 rrrrrr ………………………(3)
__NSCFString : 0x600000073640 rrrrrrtttttttt ………………………(4)
__NSCFString : 0x600000073640 rrrrrrtttttttt ………………………(5)
NSTaggedPointerString : 0xa007272727272726 rrrrrr ………………………(6)
在可变字符串,属性定义是,strong修饰的mutStr_strong,在(2)中赋值时,内存地址没有变化,同时类型还是可变字符串(__NSCFString), 但是(3)中用copy修饰的mutStr_copy,地址变化,并且类型不成不可变的字符串常亮
再看打印结果中(5) 在局部变量mutS变化后,mutStr_strong跟着变化了, mutStr_copy没有变化
这就说明,为什么自定义属性修饰时,具有可变属性的obj(NSString(NSMutableString) ,NSArray(NSMutableArray), NSDictionary(NSMutableDictionay)), 在不可变属性定义时,用copy的原因了,比如:
@property (nonatomic, copy) NSString * str;
就是为了防止在修改局部变量时,影响全局变量
问题二: 那么还有一个问题, 上面mutStr_copy,在进行[self.mutStr_copy appendFormat:@“aaaa”];
会出现崩溃,崩溃原因是没有找到appendFormat 的方法,原因是,在copy修改时的属性,其进行copy操作后,将变成不可变的
所以,在上述对可变字符串mutStr_copy修饰中,用strong, 但在赋值时,用深拷贝
操作如下:
Person.h
@property (nonatomic, strong) NSMutableString *mutStr_copy;
Person.m
self.mutStr_copy = [mutS mutableCopy];
LOG_OBJ_ADDRESS(self.mutStr_copy)
这样self.mutStr_copy 的类型还是NSMutableString,并且不会收局部变量的影响
上面的2这个整个结论是,在不可变对象(其具有可变的子类时)定义时,用copy修饰,在可变变量定义时,用strong修饰,但是用记得用mutableCopy,深拷贝
3. 对类进行拷贝构造
如果想要实现对类实现copy和mutablecopy,那么就要遵循NSCopying , NSMutableCopying
举个例子:
外部要实现这样的效果:
//深拷贝
Person *person1 = [[Person alloc] init];
person1.name = @"ddd";
person1.age = 18;
person1.mutArr = [NSMutableArray arrayWithObjects:@"122",nil];
Person *person2 = [person1 mutableCopy];
或者:
Person *person2 = [person1 copy];
Person中类的实现
Person.h
@interface Person : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong)NSMutableArray *mutArr;
@property (nonatomic, assign) int age;
@end
Person.m
拷贝构造
- (id)copyWithZone:(nullable NSZone *)zone{
Person *people = [[self class] allocWithZone:zone];
people.name = [_name copy];
people.age = _age;
people.mutArr = [_mutArr mutableCopy];
return people;
}
- (id)mutableCopyWithZone:(nullable NSZone *)zone{
Person *people = [[self class] allocWithZone:zone];
people.name = [_name copy];
people.age = _age;
people.mutArr = [_mutArr mutableCopy];
return people;
}