疑问:
我们都知道 copy
一般用来修饰 有对应可变类型的不可变对象上
,比如 NSString,NSArray 和 NSDictionary。那么为什么不推荐用 copy
去修饰 NSMutableString 和 NSMutableArray 而是用 strong
呢?
测试:
平时没怎么关注过这个问题,那么我就来测试一下。
一、先测试一下为什么 NSString 要用 copy
首先定义两个字符串属性,一个 strong
一个 copy
:
用 strong 修饰
@property (nonatomic, strong) NSString *str_strong;
用 copy 修饰
@property (nonatomic, copy) NSString *str_copy;
1、用可变字符串 NSMutableString 赋值:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *mutString = [[NSMutableString alloc] initWithFormat:@"原可变字符串"];
// 赋值
self.str_strong = mutString;
self.str_copy = mutString;
// 追加字符串
[mutString appendString:@"+++追加字符串"];
NSLog(@"\n mutString: %@, %p, %p \n str_strong: %@, %p, %p \n str_copy: %@, %p, %p \n" , mutString, mutString, &mutString, self.str_strong, _str_strong, &_str_strong, self.str_copy, _str_copy, &_str_copy);
}
打印结果如下:
mutString: 原可变字符串+++追加字符串, 0x2828ab6f0, 0x16f027af8
str_strong: 原可变字符串+++追加字符串, 0x2828ab6f0, 0x127f0cab0
str_copy: 原可变字符串, 0x2828ab8a0, 0x127f0cab8
可以看出 str_strong 和 mutString 指向对象内存是一样的,因为 strong
是 浅拷贝(指针拷贝),他们指向的都是同一个对象,地址没有变化,值当然也就一样了。
str_copy 指向对象的内存地址和他们不一样,因为 str_copy 对象使用的 copy
深拷贝,是一个新的对象,开辟了新的内存地址,不用以前的地址。
2、用不可变字符串 NSString 赋值:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [[NSString alloc] initWithFormat:@"不可变字符串"];
//进行赋值
self.str_strong = str;
self.str_copy = str;
NSLog(@"\n str: %@, %p, %p \n str_strong: %@, %p, %p \n str_copy: %@, %p, %p \n" , str, str, &str, self.str_strong, _str_strong, &_str_strong, self.str_copy, _str_copy, &_str_copy);
}
打印结果如下:
str: 不可变字符串, 0x283d55860, 0x16fbcbaf8
str_strong: 不可变字符串, 0x283d55860, 0x100a15bf0
str_copy: 不可变字符串, 0x283d55860, 0x100a15bf8
通过打印结果可以看出,str、str_strong 和 str_copy 这三者指向对象内存一样,不管是 strong
还是 copy
在这里都进行了 浅拷贝,没有重新开辟新的空间,因为这回的str 是 NSString,是不可变的。
所以一般我们是不希望我们创建的 NSString 字符串跟着之后的赋值 mutString 变化而变化的,所以都用 copy
。当然如果你希望字串的值跟着 mutString 变化,也可以使用 strong
。
但是,如果你创建的是 NSMutableString,那么不要用 copy
!
二、NSMutableString 不要用 copy
我们使用 NSMutableString 肯定是想用字符串的可变这个属性,但如果你用 copy
去修饰,那么生成的将会是不可变的,当你去调用可变方法时,程序将会崩溃!
测试:
用 copy 修饰 NSMutableString
@property (nonatomic, copy) NSMutableString *mutstr_copy;
同理,也不要对 NSMutableArray 和 NSMutableDictionary 使用 copy
修饰,不然也有可能出现崩溃。
三、总结
1、当原字符串是 NSString ,即不可变字符串时,不管是
strong
还是copy
属性的对象,都指向原对象,copy
操作也只是做了浅拷贝。2、当原字符串是 NSMutableString 时,即可变字符串时,
strong
属性只是增加了原字符串的引用计数,而copy
属性则是对原字符串做了次深拷贝,产生一个新的对象,且copy
属性对象指向这个新的对象,且这个copy
属性对象的类型始终是 NSString,而不是NSMutableString,因此其是不可变的,这时候调用可变操作,将会造成崩溃!3、 因为 NSMutableString 是 NSString 的子类,父类指针可以指向子类对象,使用
copy
的目的是为了让本对象的属性不受外界影响,这样无论给我传入是一个可变对象还是不可变对象,我本身持有的就是一个不可变的副本,这样更安全。
所以,在声明 NSString 属性时,一般我们都不希望它改变,所以大多数情况下,我们建议用 copy
,以免因可变字符串的修改导致的一些非预期问题。而在声明 NSMutableString 则需要使用 strong
。
举一反三:
把 NSMutableArray 用
copy
修饰有时就会崩溃,因为对这个数组进行了增删改操作,而copy
后的数组变成了不可变数组 NSArray ,没有响应的增删改方法,所以就崩溃了。
- 当修饰可变类型的属性时,如 NSMutableArray、NSMutableDictionary、NSMutableString,用
strong
。
当修饰不可变类型的属性时,如 NSArray、NSDictionary、NSString,用copy
。