开发中使用属性很频繁,常见的属性内存管理语义主要有 strong、copy、weak、assign,本次主要介绍下 strong 和 copy。如果你了解了深浅拷贝,那这部分内容应该很容易理解;深浅拷贝的内容可参考:iOS 你不一定了解的深浅拷贝
可以通过这样一个场景,理解 strong 和 copy 的本质区别:
- 创建一个 Person 类,声明两个属性,一个用 strong,另外一个用 copy;
#import
@interface CHIPerson : NSObject
@property (nonatomic, strong) NSString *chi_strongName;
@property (nonatomic, copy) NSString *chi_copyName;
@end
- 下面在代码中使用这个类,并对属性赋值
NSString *string = @"123456";
NSLog(@"\nstring : %@-%p\n\n", string, string);
CHIPerson *person = [[CHIPerson alloc] init];
person.chi_strongName = string;
person.chi_copyName = string;
NSLog(@"\nstrong : %@-%p\ncopy : %@-%p\n\n", person.chi_strongName, person.chi_strongName, person.chi_copyName, person.chi_copyName);
// 改变字符串的内容
string = @"789";
NSLog(@"\nstrong : %@-%p\ncopy : %@-%p\n\n", person.chi_strongName, person.chi_strongName, person.chi_copyName, person.chi_copyName);
NSLog(@"\nstring : %@-%p", string, string);
- 控制台打印结果
打印结果显示,使用 strong 和 copy 的属性内容相同,指向同一处内存区域,不可变字符串在重新赋值后,指向新的内存区域,对两个属性值没有什么影响;
到这里还没有看出 strong 与 copy 有什么不同之处,下面换种方式:
- 将不可变字符串换成可变字符串
NSMutableString *mutString = [NSMutableString stringWithFormat:@"123456"];
NSLog(@"\nstring : %@-%p\n\n", mutString, mutString);
CHIPerson *person = [[CHIPerson alloc] init];
person.chi_strongName = mutString;
person.chi_copyName = mutString;
NSLog(@"\nstrong : %@-%p\ncopy : %@-%p\n\n", person.chi_strongName, person.chi_strongName, person.chi_copyName, person.chi_copyName);
// 改变可变字符串的内容
[mutString appendString:@"789"];
NSLog(@"\nstrong : %@-%p\ncopy : %@-%p\n\n", person.chi_strongName, person.chi_strongName, person.chi_copyName, person.chi_copyName);
NSLog(@"\nstring : %@-%p", mutString, mutString);
- 再看控制台打印结果
这里就有明显的不同了,打印结果显示,使用 strong 的属性内容及地址与可变字符串保持一致,但是使用 copy 关键字的属性内容虽然相同,地址已经改变了;更改可变字符串的值后,strong 关键字的属性值跟着发生了变化,而 copy 关键字的属性值依然保持不变;
为什么会出现这样差异,实际上,使用 copy 关键字的属性,在做赋值操作的时候,会在 setter 方法中做一次 copy 操作:
- (void)setChi_copyName:(NSString *)chi_copyName
{
_chi_copyName = [chi_copyName copy];
}
这就是为什么很多有可变类型的属性,要使用 copy 关键字;copy 关键字的属性在做赋值操作时,不管传入的类型是可变的还是不可变的,属性本身相当于持有一份不可变的副本,外部变量值更改时,不会对属性产生影响,这样可以有效保证属性的封装性;
这与深浅拷贝的原理是保持一致的:对不可变对象的 copy 操作,是浅拷贝;对可变对象的 copy 操作,是深拷贝;
到这里我们可以大胆做下尝试,对 strong 关键字的属性,实现 copy 的特性,即重写 setter 方法:
- (void)setChi_strongName:(NSString *)chi_strongName
{
_chi_strongName = [chi_strongName copy];
}
- 再看控制台打印结果
可以看出,strong 关键字声明的属性也有了 copy 的特性