先说总结:
1.对于不可变属性,推荐使用copy,能防止不可变对象变成可变对象,从而防止值发生不可控变化。
2.对于可变属性,推荐使用strong,因为用copy修饰后,会变成不可变对象,再调用可变对象的函数时会crash。
1、当修饰的属性为"不可变"时:如NSString、NSArray、NSDictionary:
首先,用copy和strong声明两个不可变属性
@property (nonatomic, strong) NSString *strongStr;
@property (nonatomic, copy ) NSString *copyedStr;
1.1 初始化一个"不可变"对象,并将其赋值给strongStr和copyedStr:
NSString *str = @"123";
self.strongStr = str;
self.copyedStr = str;
NSLog(@"%p, %p, %p %@, %@, %@", str, _strongStr, _copyedStr, str, _strongStr, _copyedStr);
输出结果:
2021-09-15 20:37:38.980779+0800 test[96724:6373515] 0x10653d068, 0x10653d068, 0x10653d068 123, 123, 123
【可以看出:对于"不可变"对象,copy和strong指向的都是str的内存地址,copy对于不可变对象进行的是浅拷贝。】
此时,对str进行修改:
str = @"456";
NSLog(@"%p, %p, %p %@, %@, %@", str, _strongStr, _copyedStr, str, _strongStr, _copyedStr);
输出结果:
2021-09-15 20:37:38.981080+0800 test[96724:6373515] 0x10653d0a8, 0x10653d068, 0x10653d068 456, 123, 123
【可以看出:copy、strong指向的内存地址和内存地址中的值都没变,str重新开辟内存地址进行了修改。因为他们原本指向的内存地址的值是不可变的,str想要修改就得自立山头。】
1.2 初始化一个"可变"对象,并将其赋值给strongStr和copyedStr:
NSMutableString *mulStr = [[NSMutableString alloc] initWithString:@"PPP"];
self.strongStr = mulStr;
self.copyedStr = mulStr;
NSLog(@"%p, %p, %p %@, %@, %@", mulStr, _strongStr, _copyedStr, mulStr, _strongStr, _copyedStr);
输出结果:
2021-09-15 20:49:46.263158+0800 test[97029:6382020] 0x600000001fb0, 0x600000001fb0, 0x600000002a80 PPP, PPP, PPP
【可以看出:strongStr指向mulStr的内存地址,而且内存地址的值是个NSMutableString可变类型;而copyedStr发生深拷贝指向另一块NSString类型的内存地址】
此时,对mulStr的值进行修改:
[mulStr appendString:@"++"];
NSLog(@"%p, %p, %p %@, %@, %@", mulStr, _strongStr, _copyedStr, mulStr, _strongStr, _copyedStr);
输出结果:
2021-09-15 20:49:46.263429+0800 test[97029:6382020] 0x600000001fb0, 0x600000001fb0, 0x600000002a80 PPP++, PPP++, PPP
【可以看出:尽管我们并没有直接对strongStr的值进行修改,但它还是发生了变化(因为指向了mulStr的内存地址),这正是危险之处;copyedStr的值没有发生变化,因为其内存地址跟mulStr是不同的,所以不可变对象用copy修饰是安全的】
// =========== 分隔符 ============
2、当修饰的属性为“可变”时:如NSMutableString、NSMutableArray、NSMutableDictionary:
首先,用copy和strong声明两个可变属性:
@property (nonatomic, strong) NSMutableString *strongMulStr;
@property (nonatomic, copy ) NSMutableString *copyedMulStr;
2.1 初始化一个"可变"对象,并将其赋值给strongMulStr和copyedMulStr:
NSMutableString *mulStr = [[NSMutableString alloc] initWithString:@"abc"];
self.strongMulStr = mulStr;
self.copyedMulStr = mulStr;
NSLog(@"%p, %p, %p %@, %@, %@", mulStr, _strongMulStr, _copyedMulStr, mulStr, _strongMulStr, _copyedMulStr);
输出结果:
2021-09-15 21:17:03.368775+0800 test[97765:6402607] 0x6000003860d0, 0x6000003860d0, 0xcf1bace00b0cb946 abc, abc, abc
【可以看出:mulStr和strong对象指向同一块内存地址,而copy修饰的可变对象此时进行了深拷贝重新申请了一块内存地址】
此时,修改mulStr的值:
[mulStr appendFormat:@"def"];
NSLog(@"%p, %p, %p %@, %@, %@", mulStr, _strongMulStr, _copyedMulStr, mulStr, _strongMulStr, _copyedMulStr);
输出结果:
2021-09-15 21:17:03.369691+0800 test[97765:6402607] 0x6000003860d0, 0x6000003860d0, 0xcf1bace00b0cb946 abcdef, abcdef, abc
【可以看出:由于mulStr内存地址的值是NSMutableString可变类型,所以直接在原本内存地址进行修改,导致指向这块内存地址的_strongMulStr也跟着改变,而_copyedMulStr是另一块内存所以不受影响。】
2.2 初始化一个"不可变"对象,并将其赋值给strongMulStr和copyedMulStr:
NSString *str = @"PPP";
self.strongMulStr = str;
self.copyedMulStr = str;
NSLog(@"%p, %p, %p %@, %@, %@", str, _strongMulStr, _copyedMulStr, str, _strongMulStr, _copyedMulStr);
输出结果:
2021-09-15 21:21:06.253837+0800 test[97867:6405460] 0x10fea6068, 0x10fea6068, 0x10fea6068 PPP, PPP, PPP
【可以看出:_strongMulStr和_copyedMulStr都指向了str的内存地址,并且内存地址的值是个NSString不可变类型】
此时,修改str的值:
str = @"PPP++";
NSLog(@"%p, %p, %p %@, %@, %@", str, _strongMulStr, _copyedMulStr, str, _strongMulStr, _copyedMulStr);
输出结果:
2021-09-15 21:21:06.254076+0800 test[97867:6405460] 0x10fea60a8, 0x10fea6068, 0x10fea6068 PPP++, PPP, PPP
【可以看出:①str的内存地址重新申请了,因为原本的内存地址的值是NSString不可变类型,想要重新赋值就要申请新的内存;②_strongMulStr和_copyedMulStr的值没有变,因为str重新申请了一块内存进行赋值,对他们没有影响。】
再看个东西,如果对_strongMulStr和_copyedMulStr进行修改:(会crash)
NSString *str = @"PPP";
self.strongMulStr = str; //指向了str这个不可变内存地址
self.copyedMulStr = str; //指向了str这个不可变内存地址
[self.strongMulStr appendString:@"~~"];//crash
[self.copyedMulStr appendString:@"~~"];//crash
输出结果:
直接crash。为什么修改NSMutableString这个可变对象会crash呢?
原因不同:
对于strongMulStr,虽然定义的是NSMutableString属性,但是由于赋值时指向了str这个不可变对象而变成了NSString,而NSString里没有appendString:这个函数,所以报错;
对于copyedMulStr,虽然定义的是NSMutableString属性,但是用copy修饰后进行了深拷贝,变成了NSString类型,也不能再调用appendString:函数。
总结:
1.对于不可变属性,推荐使用copy,能防止不可变对象变成可变对象,从而防止值发生不可控变化。
2.对于可变属性,推荐使用strong,因为用copy修饰后,会变成不可变对象,再调用可变对象的函数时会crash。