1.strong和copy的区别
通常情况下,我们在定义字符串属性的时候,会用到两种修饰词:strong和copy。但是比较规范的写法是用copy,也就是说copy要比strong更好,到底copy好在哪里?我们下面用实例来说明。
@interface ViewController ()
@property (nonatomic, copy) NSString* str1;
@property (nonatomic, strong) NSString* str2;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableString* tempStr = [@"hello" mutableCopy];
self.str1 = tempStr;
self.str2 = tempStr;
NSLog(@"str1 = %@, str2 = %@",self.str1, self.str2);
[tempStr appendFormat:@"world"];
NSLog(@"str1 = %@, str2 = %@",self.str1, self.str2);
}
在上面的代码中,分别给str1和str2赋值为同一个可变字符串变量(有人可能会疑问,为什么要赋一个可变字符串,而不是一个NSString类型的?如果赋值一个NSString类型的字符串,用strong和copy也就没有什么区别了,这一点我们后面会进行总结,在这儿我们先看它们之间的差异),执行上面的代码,打印的结果如下图:
通过打印的结果我们发现:当我改变tempStr的值的时候,str1的值没有发生变化(这个是用copy修饰的), 但是str2的值发生了改变(这个是用strong修饰的),由此我们可以假设一下,str1和tempStr不是指向同一块儿内存,而str2和tempStr指向同一块儿内存,下面来验证一下:
NSMutableString* tempStr = [@"hello" mutableCopy];
self.str1 = tempStr;
self.str2 = tempStr;
NSLog(@"temStr = %p, self.str1 = %p, self.str2 = %p",tempStr,self.str1,self.str2);
NSLog(@"str1 = %@, str2 = %@",self.str1, self.str2);
[tempStr appendFormat:@"world"];
NSLog(@"str1 = %@, str2 = %@",self.str1, self.str2);
只是在原来的代码中加了一行指针打印,打印结果如下:
果然和我们假设的一致,str1和tempStr不指向同一块儿内存,而str2和tempStr指向了同一块儿内存。那么这样就会出现一个问题:本来我们定义的str2是一个NSString类型,这就意味着我们并不想str2发生改变,但是当外界赋给它一个可变的值的时候,它却因为外界值的修改而被修改,这是我们所不愿看到的。所以上面的情况用copy修饰会更好。
2.NSArray是不是也需要用copy来修饰?
看了上面的情况,是不是有人会联想到像 NSArray和NSDictionary这样的可以赋给一个可变量的属性,是不是也应该用copy来修饰?下面我们还是用实例来说明。
@interface ViewController ()
@property (nonatomic, copy) NSArray* array1;
@property (nonatomic, strong) NSArray* array2;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray* tempArray = [@[@"hello"] mutableCopy];
self.array1 = tempArray;
self.array2 = tempArray;
NSLog(@"tempArray = %p, array1 = %p, array2 = %p, ",tempArray,self.array1,self.array2);
NSLog(@"array1 = %@, array2 = %@",self.array1,self.array2);
[tempArray addObject:@"world"];
NSLog(@"array1 = %@, array2 = %@",self.array1,self.array2);
}
打印结果如下:
从打印的结果我们依然可以得出: array1(也就是用copy修饰的)和tempArray不指向同一块儿内存,array2(也就是用strong修饰的)和tempArray指向了同一块儿内存,所以当我们修改tempArray的时候,array2的值也被修改了,这跟我们上面定义成NSArray,也就是不可变类型相违背,所以当我们定义一个像NSArray和NSDictionary这样有对应可变类型的属性的时候,都应该用copy。
可能到这儿还是有人有疑问,如果我定义的属性本身就是一个可变类型的,比如NSMutableArray,那么我是应该用strong还是copy呢?如果不出意料的话,大家用的都是strong,下面我们测一下为什么strong又比copy好。还是用实例说话。
@interface ViewController ()
@property (nonatomic, copy) NSMutableArray* array3;
@property (nonatomic, strong) NSMutableArray* array4;
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray* tempArray = [NSMutableArray arrayWithObject:@"hello"];
self.array3 = tempArray;
self.array4 = tempArray;
NSLog(@"tempArray = %p,array3 = %p,array4 = %p",tempArray,self.array3,self.array4);
[tempArray addObject:@"world"];
NSLog(@"array3 = %@,array4 = %@,",self.array3,self.array4);
}
打印的结果如下:
从打印的结果可以看出,array3和tempArray不是指向同一块儿内存,array4和tempArray指向同一块儿内存,所以当修改tempArray的值的时候,array4的值也跟着改了。而array4本身就是一个NSMutableArray类型,就意味着是可以被修改的,所以用strong会更好。
-
总结一下上面的类容
用copy还是strong主要取决于开发者的意图:当你不允许让这个属性的值被外界更改,就用copy;当你允许这个属性的值被外界所更改,就用strong。
3. copy和mutablecopy
对于copy和mutablecopy的区别,需要分为两种情况来谈:给不可变属性赋值和给可变属性赋值。下面先看给不可变属性赋值的情况,依然用实例说话:
@interface ViewController ()
@property (nonatomic, copy) NSString* str3;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString* tempStr = @"hello";
self.str3 = [tempStr copy];
NSLog(@"tempStr_p1 = %p, str3_p1 = %p",tempStr,self.str3);
self.str3 = [tempStr mutableCopy];
NSLog(@"tempStr_p2 = %p, str3_p2 = %p",tempStr,self.str3);
}
打印结果如下:
- 由图打印的结果可以得出如下结论
str3是不可变的类型,[tempStr copy]也是不可变的类型,把tempStr赋给str3,也就是把一个不可变的赋给一个不可变的,既然两个都不可变,也就是都不存在被修改的风险,所以用同一块儿内存就行了,没有必要再搞一块儿内存,所以会有上面的 tempStr_p1=str3_p1;
但是[tempStr mutableCopy]是一个可变的类型,把[tempStr mutableCopy]赋给str3,也就是把一个可变的赋给了一个不可变的,但是这样就会出现一个问题:当可变的那个变量的值发生改变的时候,可能会导致不可变的那个变量的值也跟着改变,这就违背了开发者的意图,所以就要给str3重新分配一份儿内存,也就有了上面的 tempStr_p12 != str3_p2。
下面再看看给可变类型的属性赋值的情况,还是用实例说话:
@interface ViewController ()
@property (nonatomic, strong) NSMutableString* str4;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableString* tempStr = [NSMutableString stringWithString:@"hello"];
self.str4 = [tempStr copy];
NSLog(@"tempStr_p1 = %p, str4_p1 = %p",tempStr,self.str4);
self.str4 = [tempStr mutableCopy];
NSLog(@"tempStr_p2 = %p, str4_p2 = %p",tempStr,self.str4);
}
打印结果如下图
由图打印的结果可以得出如下的结论
str4是可变的类型,[tempStr copy]其实是一个不可变的类型,把[tempStr copy]赋给str4,其实是把一个不可变的赋给了一个可变的,如果这两个还共用一块儿内存的话,当str4的值发生了改变,会导致[tempStr copy]这个不可变的变量的值也跟着变化,这样就矛盾了,所以就有了上面的 tempStr_p1 != str4_p1,也就是给str4重新搞一块儿内存,让str4在自己的那块儿内存中随便折腾而不影响别人;
但是[tempStr mutableCopy]是一个可变的类型,把[tempStr mutableCopy]赋给str4,其实是把一个可变的赋给了一个可变的,那么可能有人会有疑问,既然都是可变的,那为何不共用同一块儿内存呢,为何 tempStr_p2 != str4_p2呢?我的理解是这样的:虽然[tempStr mutableCopy]是可变的,但是我不希望你str4来改变我,我要变我自己会变,所以还是给str4搞了一块儿新的内存,让str4自己玩自己的,不要去影响别人。本文属原创文章,转载请注明出处,谢谢!