在声明property属性时,如果是一个NSString的变量,那么绝大多数情况下都要用到copy,这是为什么呢?这是因为用了copy之后更加安全,减少了代码之间的耦合度,属性的值更不容易被改变。
首先要明白两个概念,深复制与浅复制的区别:
深复制:复制对象的内存区域和指针,复制后的对象与原有对象相互独立
浅复制:仅仅复制指针,复制后两个指针指向同一块内存区域,复制后的对象与原有对象相互影响
明白了这两个概念就更容易理解了,先通过如下的例子直观了解一下copy的好处:
首先定义了一个HITStudent类,里面包含了一个用copy修饰的stuName属性和一个被assign修饰的stuNumber属性,此处nonatomic是线程安全的意思,如果不写默认为atomic。
#import <Foundation/Foundation.h>
@interface HITStudent : NSObject
@property (nonatomic,copy)NSString *stuName;
@property (nonatomic,assign)NSString *stuNumber;
@end
在主函数中先创建一个名为stu1的HITStudent对象,并把该对象的stuName属性和stuNunber属性分别赋值为xgc和123,之后我们对可变字符串name和number进行修改,再次打印stu1的两个属性我们发现用了copy修饰的stuName属性没有变化,但是用assign修饰的stuNumber属性的值却被改变了。
#import <Foundation/Foundation.h>
#import "HITStudent.h"
int main(int argc,constchar * argv[]) {
@autoreleasepool {
HITStudent *stu1 = [[HITStudentalloc]init];
NSMutableString *name = [NSMutableStringstringWithFormat:@"xgc"];
NSMutableString *number = [NSMutableStringstringWithFormat:@"123"];;
stu1.stuName = name;
stu1.stuNumber = number;
NSLog(@"学生的姓名为:%@",stu1.stuName);
NSLog(@"学生的号码为:%@",stu1.stuNumber);
[name appendString:@"HIT"];
[number appendString:@"456"];
NSLog(@"更改后学生的姓名为:%@",stu1.stuName);
NSLog(@"更改后学生的号码为:%@",stu1.stuNumber);
}
return0;
}
2016-04-18 10:44:06.025 copy[1230:46766]学生的姓名为:xgc
2016-04-18 10:44:06.027 copy[1230:46766]学生的号码为:123
2016-04-18 10:44:06.027 copy[1230:46766]更改后学生的姓名为:xgc
2016-04-18 10:44:06.027 copy[1230:46766]更改后学生的号码为:123456
Program ended with exit code: 0
由此我们可以得出结论:使用copy修饰NSString的时候,如果可变字符串改变了,被可变字符串的属性不会改变
下面我们来分析一下原理:
assign和copy实际上是通过set方法来进行内存管理,当我们在ARC模式下使用assign时,系统会自动生成如下set方法:
-(void)setStuName:(NSString *)stuName
{
_stuName = stuName;
}
而使用copy时会生成如下set方法:
-(void)setStuName:(NSString *)stuName
{
if (_stuName != stuName) {
[_stuName release];
_stuName = [stuNamecopy];
}
具体来说,assign是直接赋值,让属性与原来的可变字符串指向了同一块内存区域,这样的话当可变字符串指向的内存区域的值改变的时候,属性的值自然也跟着改变,赋值相当于浅复制;
而copy的本质是先release原有的值,再复制该内存所储存的内容,可以理解为先做了一次release,又做了一次retain,但之后得到了一个全新的对象,拥有不同的内存区域,因此改变原有的字符串不会对属性的值产生影响,copy赋值相当于深复制。