注册

NSMutableString 不要用 copy

疑问:

我们都知道 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;

aa2d57cd609f3560f050f34f16c81a40.png

同理,也不要对 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


作者:凡几多
链接:https://www.jianshu.com/p/5d138efee024

0 个评论

要回复文章请先登录注册