@qidiandasheng
2019-07-04T01:41:05.000000Z
字数 4111
阅读 2144
iOS理论
我们在声明一个NSString属性时,其内存相关的特性,我们有两种选择:strong和copy。一般我们都会使用copy,但是为什么使用copy你知道吗?
稍微了解一点的人可能就会觉得这不就是深拷贝和浅拷贝嘛,使用copy就是深拷贝,使用strong就是浅拷贝。
然而真的是这样吗?下面我们来写一个例子:
@interface TestStringClass ()@property (nonatomic, strong) NSString *strongString;@property (nonatomic, copy) NSString *copyedString;@end
- (void)test {NSString *originString = [NSString stringWithFormat:@"abc"];self.strongString = originString;self.copyedString = originString;NSLog(@"origin string: %p, %p", originString, &originString);NSLog(@"strong string: %p, %p", _strongString, &_strongString);NSLog(@"copy string: %p, %p", _copyedString, &_copyedString);}
你觉得输出会是什么呢?指针地址肯定是不一样的。普通的想法是认为strong是浅拷贝,copy是深拷贝。那么_strongString和originString的内存地址是一样的,_copyedString的内存地址是不一样的。
下面我们来看看实际输出是什么样的:
2015-08-30 14:37:50.573 test[19357:5912951] origin string: 0xa000000006362613, 0x7fff50bfbc482015-08-30 14:37:50.574 test[19357:5912951] strong string: 0xa000000006362613, 0x7fe44961d7902015-08-30 14:37:50.574 test[19357:5912951] copy string: 0xa000000006362613, 0x7fe44961d798
好像跟我们想的不一样?内存地址都是一样的。
下面我们把NSString换成NSMutableString看看,将
NSString *originString = [NSString stringWithFormat:@"abc"];
改为:
NSMutableString *originString = [NSMutableString stringWithFormat:@"abc"];
输出结果:
2015-08-30 14:51:46.119 test[20229:5955951] origin string: 0x7fc27b47ff60, 0x7fff5c14cc482015-08-30 14:51:46.120 test[20229:5955951] strong string: 0x7fc27b47ff60, 0x7fc27b6433a02015-08-30 14:51:46.120 test[20229:5955951] copy string: 0xa000000006362613, 0x7fc27b6433a8
我们看到originString跟_strongString内存是一样的,_copyedString内存地址是不一样的。
我们现在来想一下原因,当我们使用
NSString的时候其实是不希望他改变的,那么我们一般情况下是使用copy,希望他进行深拷贝,那源字符串修改就不会影响到_copyedString了。但是如果源字符串也是NSString不可变的呢,那其实就算是浅拷贝也不会有什么影响了。
所以系统可能就在当源字符串为不可变类型时,你属性的内存特性为copy其实也只进行浅拷贝。当源属性为可变类型时,才进行深拷贝。
所以我们建议在使用
NSString属性时使用copy,避免可变字符串的修改导致的一些非预期问题。
上面这句话我们会常常看到,那么很多人问我了,这种情况什么情景下会出现呢?
我这里举一个最简单的例子,有个ViewController他刚进来的时候有个原价,这是一个原价那当然是不可变咯。
@interface GoodsViewController : UIViewController@property(nonatomic, strong)NSString *orginPrice;@end
NSMutableString *_mutablePrice = [NSMutableString stringWithFormat:@"100"];GoodsViewController *goodsVC = [[GoodsViewController alloc] init];goodsVC.orginPrice = _mutablePrice;[self.navigationController pushViewController:goodsVC animated:YES];
我们先假设orginPrice为strong:
我们已经进入GoodsViewController,这个商品的原价就是100,我们不希望他发生改变。这时可能哪里发了个通知,_mutablePrice加100变成了200。
而GoodsViewController也接收到了通知,准备把orginPrice加100变为200。但是这时候因为是strong只是浅拷贝,orginPrice在_mutablePrice变为200的那一刻已经改为200,这时如果你再加100,其实orginPrice就变成300了,这就不是我们想看到的了。
那如果orginPrice为copy呢:
这时发生了深拷贝,_mutablePrice的改变跟orginPrice没有关系了,所以不用担心产生上面那样的问题。
因为使用copy就是深拷贝了一个不可变的NSString对象。这时如果对这个对象进行可变操作,会产生崩溃。
@property(nonatomic, copy)NSMutableString *copyString;//这句产生崩溃[copyString appendString:@"齐滇大圣"];
等价于
NSMutableString *mutableString = [[NSMutableString alloc] initWithFormat:@"我是"];NSMutableString *copyString = [mutableString copy];//这句产生崩溃[mystring appendString:@"齐滇大圣"];
若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
NSCopying协议中的声明的方法只有一个- (id)copyWithZone:(NSZone *)zone。当我们的类实现了NSCopying协议,通过类的对象调用copy方法时,copy方法就会去调用我们实现的- (id)copyWithZone:(NSZone *)zone方法,实现拷贝功能。实现代码如下所示:
@implementation PersonModel {NSString *_nickName;}- (id)copyWithZone:(NSZone *)zone{PersonModel *model = [[[self class] allocWithZone:zone] init];model.firstName = self.firstName;model.lastName = self.lastName;//未公开的成员model->_nickName = _nickName;return model;}
NSMutableCopying中对于的声明方法为- (id)mutableCopyWithZone:(NSZone *)zone。跟NSCopying的区别就是返回的对象是否是可变类型。
下面我们来写个例子看看如何运用:
PersonModel *person1 = [[PersonModel alloc] init];person1.firstName = @"郑";PersonModel *person2 = person1;person2.firstName = @"吴";NSLog(@"%@",person1.firstName);
输出值:吴
因为这个person1对象根本没有被深拷贝,所有person2改变的时候,person1也被改变了。
我们修改代码如下:
PersonModel实现NSCopying协议
@interface PersonModel : NSObject<NSCopying>@property(nonatomic, copy)NSString *firstName;@end@implementation PersonModel- (id)copyWithZone:(NSZone *)zone{PersonModel *person = [[[self class] allocWithZone:zone] init];person.firstName = _firstName;return person;}@end
PersonModel *person1 = [[PersonModel alloc] init];person1.firstName = @"郑";PersonModel *person2 = [person1 copy];person2.firstName = @"吴";NSLog(@"%@",person1.firstName);
输出值:郑