深入理解 retain,copy,mutableCopy

来源:互联网 发布:网络交友有哪些弊端 编辑:程序博客网 时间:2024/06/06 01:02

一,retain和copy的区别:

众所周知,成员属性,retain修饰,引用计数加1,copy修饰,重新开辟内存复制对象。在官方SDK中,NSString属性都是copy,而不是retain。这么做的原因就是,如果赋值的是一个NSMutableString 类型的变量,那么当你在外部修改值时,你的属性值也跟着改变了。
例1:
@property(nonatomic,retain)NSString *str ;@property(nonatomic,copy)NSString *str2 ;    <span style="color:#ff0000;">NSString</span> *tmp1=[[NSString alloc] initWithFormat:@"tmp1"];    self.str=tmp1;    NSLog(@"%@,address:%p,retainCount:%d",tmp1,tmp1,tmp1.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str,self.str,self.str.retainCount);    [tmp1 release];        <span style="color:#ff0000;">NSString</span> *tmp2=[[NSString alloc] initWithFormat:@"tmp2"];    self.str2=tmp2;    NSLog(@"%@,address:%p,retainCount:%d",tmp2,tmp2,tmp2.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str2,self.str2,self.str2.retainCount);    [tmp2 release];
输出结果:

[1619:607] tmp1,address:0xca4d500,retainCount:2

[1619:607] tmp1,address:0xca4d500,retainCount:2

[1619:607] tmp2,address:0xca11360,retainCount:2

[1619:607] tmp2,address:0xca11360,retainCount:2

由以上可以看出,NSString类型的属性,不管是retain还是copy,当用一个NSString 不可变类型变量赋值时,其安全性、内存管理都一样;
例2:
@property(nonatomic,retain)NSString *str ;@property(nonatomic,copy)NSString *str2 ;    <span style="color:#ff0000;">NSMutableString</span> *tmp1=[[NSMutableString alloc] initWithFormat:@"tmp1"];    self.str=tmp1;    NSLog(@"%@,address:%p,retainCount:%d",tmp1,tmp1,tmp1.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str,self.str,self.str.retainCount);    [tmp1 release];        <span style="color:#ff0000;">NSMutableString</span> *tmp2=[[NSMutableString alloc] initWithFormat:@"tmp2"];    self.str2=tmp2;    NSLog(@"%@,address:%p,retainCount:%d",tmp2,tmp2,tmp2.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str2,self.str2,self.str2.retainCount);    [tmp2 release];
输出结果:

[1643:607] tmp1,address:0xc8389c0,retainCount:2

[1643:607] tmp1,address:0xc8389c0,retainCount:2

[1643:607] tmp2,address:0xc836500,retainCount:1

[1643:607] tmp2,address:0xc824130,retainCount:1

由以上可以看出,NSString类型的属性,用retain修饰,当用一个NSMutableString 可变类型变量赋值时,其内存地址一样,只有一份拷贝;用copy修饰时,内存地址变了,此时拥有两份拷贝。为了更好的看出区别,我们作如下测试:

例3:

@property(nonatomic,retain)NSString *str ;@property(nonatomic,copy)NSString *str2 ;    <span style="color:#ff0000;">NSMutableString</span> *tmp1=[[NSMutableString alloc] initWithFormat:@"tmp1"];    self.str=tmp1;    <span style="color:#3333ff;">[tmp1 appendString:@"AAA"];</span>    NSLog(@"%@,address:%p,retainCount:%d",tmp1,tmp1,tmp1.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str,self.str,self.str.retainCount);    [tmp1 release];        <span style="color:#ff0000;">NSMutableString</span> *tmp2=[[NSMutableString alloc] initWithFormat:@"tmp2"];    self.str2=tmp2;    <span style="color:#3333ff;">[tmp2 appendString:@"BBB"];</span>    NSLog(@"%@,address:%p,retainCount:%d",tmp2,tmp2,tmp2.retainCount);    NSLog(@"%@,address:%p,retainCount:%d",self.str2,self.str2,self.str2.retainCount);    [tmp2 release];
输出结果:

[1673:607] tmp1AAA,address:0xce55930,retainCount:2

[1673:607] tmp1AAA,address:0xce55930,retainCount:2

[1673:607] tmp2BBB,address:0xce55970,retainCount:1

[1673:607] tmp2,address:0xce559b0,retainCount:1

由以上可以看出,当用可变类型变量赋值成员属性后,再对可变类型的变量进行了修改,那么retain修饰的,也同样会被修改;copy修饰的,因为是对象的拷贝,所以,不会同步被外界变量所修改。

综合以上实验,可以得出结论,如果为成员变量赋值时,使用的是不可变的变量,那么retain和copy从内存管理上来说,其效果是一样的,没有什么使用的区别。如果使用的是可变变量对属性进行赋值,那么retain修饰的属性值就有可能被修改的可能(这往往不是我们想要的结果),而使用copy修饰的属性值就不会被修改。故:官方的sdk中,NSString 类型都是copy修饰。 

建议:NSString 属性,请使用copy修饰。(当然,如果你想让你的属性值跟着变量的变化而变化,那就用retain吧)

二,copy和mutableCopy的区别

如果对一不可变对象进行复制,copy是指针复制(浅拷贝),mutableCopy是对象复制(深拷贝);
如果对一可变对象进行复制,copy和mutableCopy都是对象复制(深拷贝)。
mutableCopy才是真正意义上的复制,不管是可变对象还是不可变对象,系统都为其分配了新内存。copy对不可变对象复制的是指针,其实也是出于对内存利用的考虑。知道这一点即可,不必深究。
例:
    NSString *tmp1=[[NSString alloc] initWithFormat:@"tmp1"];    NSString *copy1= [tmp1 copy];    NSMutableString *mutableCopy1=[tmp1 mutableCopy];    NSLog(@"tmp1:%p,copy1:%p,mutableCopy1:%p",tmp1,copy1,mutableCopy1);    [tmp1 release];        NSMutableString *tmp2=[[NSMutableString alloc] initWithFormat:@"tmp2"];    NSString *copy2= [tmp2 copy];    NSMutableString *mutableCopy2=[tmp2 mutableCopy];    NSLog(@"tmp2:%p,copy2:%p,mutableCopy2:%p",tmp2,copy2,mutableCopy2);    [tmp1 release];
输出结果:

[1805:607] tmp1:0xc833dd0,copy1:0xc833dd0,mutableCopy1:0xc833db0

[1805:607] tmp2:0xc826fb0,copy2:0xc835490,mutableCopy2:0xc8354a0

建议:记住一点即可,如果复制的是不可变类型,那么copy并没有开辟新的内存,而是进行了一次浅拷贝。当然了,在实际应用中,copy返回的是不可变对象,mutableCopy返回的是可变对象,除此之外,其他都不影响使用。

三、retainCount = -1 的疑惑

示例:
    NSString *tmp=[[NSString alloc] initWithString:@"aaa"]; //这里等同于: tmp=@"aaa";    NSLog(@"%d",tmp.retainCount);    [tmp retain];    NSLog(@"%d",tmp.retainCount);    [tmp release];    NSLog(@"%d",tmp.retainCount);    _str=tmp;  //str为retain修饰的成员属性    _str2=tmp; //str2为copy修饰的成员属性    NSLog(@"%d,%d",_str.retainCount,_str2.retainCount);
输出结果:

2016:607] -1

[2016:607] -1

[2016:607] -1

[2016:607] -1,-1

这里,我们可以看到输出结果都为-1,这是因为 @"aaa" 其实是分配在常量存储区,系统不会对齐回收,也不会对齐进行引用计数。
注意:这里如果换成 initWithFormat:,其引用计数就是1了。这一点,一定要注意。

四、NSString、NSArray等赋值时注意

赋值时,请使用点语法,这样,retainCount会自动加1,如果直接给属性变量赋值的话,retainCount是不会自动加1的。(想想就明白了,retain,copy都是通过重写属性的set方法来实现的,如果直接给属性变量赋值,就不会走set方法,自然retainCount也就不会加1了。)

NSArray *tmpArray=@[@1,@2]; // tmpArray retainCount =1;

self.array=tmpArray;  // 赋值方法1: self.array retainCount =2;  推荐

_array=tmpArray;  //赋值方法2: self.array retainCount =1; 不推荐



0 0
原创粉丝点击