#修饰词retain、assign
***retain、assign、copy在@property中的使用实际上是通过控制set方法进行内存管理。下面详细介绍:***
1. retain
例如:
```
@class Dog
@property (nonatomic,retain) Dog *dog;
```
**注:nonatomic与atomic相对应,涉及线程,nonatomic相对atomic来说性能高,而声明属性时一般认为atomic,故需在此申明nonatmic**
这里使用了retain,那么set方法中,究竟如何体现
```
- (void)setDog:(Dog *)dog
{
if (_dog != dog) { //判断是否需要重新赋值
[_dog release]; //释放旧引用,计数器-1
_dog = [dog retain]; //重新赋值,计数器+1
}
}
```
2. assign:
例如:
```
@property (nonatomic,assign) int count;
```
这里使用了assign,那么在set方法中,究竟如何体现
```
- (void)setCount:(int)count
{
_count = count;
}
```
3. copy:
例如:
```
@property(nonatomic,copy)NSString *str;
```
这里使用了copy,那么在set方法中
```
- (void)setStr:(NSString *)str
{
if(_str != str) { //判断是否需要重新赋值
[_str release]; //释放旧引用,计数器-1
_str = [str copy]; //重新赋值,使用copy
}
}
```
##总结:
1. retain:先release旧值,在retain新值,在上例中_dog与dog最终指向同一块内存区域。
2. assign:直接赋值,不考虑内存管理。
3. copy:先release旧值,再copy新值,copy的本质为复制该内所存储的内容,重新创建一个对象赋给其相同的内容,很明显,在copy这个过程中也发生了一次retain,不过这是个全新的对象。在上例中,_str与str最终指向了不同的区域,但其内容一样。
4. 从retain、assign、copy的特点中:
retain一般适用于OC中的对象
assign一般适用于非OC对象,如int等普通类型
copy一般适用于NSString等不可变的对象,因为是重新创建了对象,并且内容不变,因此不用担心后面的操作会对该属性的值产生影响。
##实例分析:
假设str为对象p的属性
```
@property (nonatomic,copy)NSString *str;
NSMutableString *s = [[NSMutableString alloc] initWithString:@"hello"];
p.str = s; //此时,str的值为@”hello“
[s appendString:@"world"]; //此时,s的值是”hello world“,但是str的值依然为”hello“。
```
但是如果开始时str的申明为:
```
@property(nonatomic,retain)NSString *str;
```
那么,在进行完[s appendString:@"world"]之后,str的值将变为”hello world“。因为str与s共用一块内存,内容完全相同,而s是可以改变的,所以s改变后,str也将改变。
copy:建立一个索引计数为1的对象,然后释放就对象 对NSString
对NSString它指出,在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
对其他NSObject和其子类
对参数进行release旧值,再retain新值
指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数)。
***注意:把对象添加到数组中时,引用计数将增加对象的引用计数次数+1***
retain的实际语法为:
- (void)setName:(NSString *)newName {
if (name != newName) {
[name release];
name = [newName retain];
}
}
copy与retain:
Copy其实是建立了一个相同的对象,而retain不是:
比如一个NSString对象,地址为0X1111,内容为@”STR“
Copy到另一个NSString之后,地址为0X2222,内容相同新的对象retain为1,旧有对象没有变化
retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1
也就是说retain是指针拷贝,copy是内容拷贝。
retain的set方法应该是浅复制,copy的set方法应该是深复制了
copy另一个用法:
copy是内容的拷贝,对于像NSString的确实这样
但是,如果是copy的是一个NSArray呢?
NSArray *array = [NSArray arrayWithObjects:@"hello",@"world",@"baby"];
NSArray *array2 = [array copy];
这个时候,系统的确是为array2开辟了一块内存空间,但是我们要认识到的是,array2中的每个元素,只是copy了指向array中相对应元素的指针。这便是所谓的”浅复制“。
assign:简单辅值,不更改索引计数
对基础数据类型(例如NSInteger,CGFloat)和C数据类型(int,float,double,char等)适用简单数据类型
此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明白指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。
weak和strong属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease操作,ARC会自动为你做好这些操作,但是你需要在对象属性上使用weak和strong,其中strong就相当于retain属性,而weak相当于assign。
strong关键字与retain相似,引用计数自动+1,用实例更能说明一切
@property(nonatomic,strong)NSString *string1;
@property(nonatomic,string)NSString *string2;
self.string1 = @"String1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String2 = %@",self.string2);
结果是:String2 = String1
***由于string2是strong定义的属性,所以引用计数+1,使得它们指向同一地址内容为:@”String1“,不可变字符串每次赋值都会重新开辟新地址。***
接着我们来看weak关键字:
如果这样声明两个属性:
@property(nonatomic,strong)NSString *string1;
@property(nonatomic,weak)NSString *string2;
self.string1 = @"String1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String2 = %@",self.string2);
结果是:String2 = null
***分析下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1 = nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的控件释放后,都要讲指针赋为NULL。在这儿用weak关键字帮我们做了这一步。***
##copy
特点:
1. 修改源文件的内容,不会影响副文本;
2. 修改副文本的内容,不会影响源文件;
OC中copy的作用是:利用一个源对象产生一个副本对象
特点:
1. 修改源对象的属性和行为,不会影响副本对象;
2. 修改副本对象的属性和行为,不会影响源对象。
如何使用copy功能
一个对象可以调用copy或mutableCopy方法来创建一个副本对象。
1. copy:创建的时不可变副本(NSString、NSArray、NSDictionary)。
2. mutableCopy:创建的可变副本(NSMutableString、NSMutableArray、NSMutableDictionary)。
使用copy功能的前提:
1. copy:需要遵守NSCopying协议,实现copyWithZone:方法。
```
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
```
2. mutableCopy:需要遵守NSMutableCopy协议,实现mutableCopyWithZone:方法
```
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
```
###深复制和浅复制的区别:
**深拷贝**
特点:
1. 源对象和副本对象是不同的两个对象;
2. 源对象引用计数器不变,副本对象计数器为1(因为是新产生的)。
本质:产生了新对象
**浅拷贝**
特点:
1. 源对象和副本对象是同一对象;
2. 源对象(副本对象)引用计数+1,相当于做一次retain操作。
本质:没有产生新的对象。
常见赋值如下:![](http://oh6yavwvf.bkt.clouddn.com/00027.png)
***只有源对象和副本对象都不可变时,才是浅复制,其他都是深复制。***
```
/**
NSMutableString调用mutablecopy:深复制
*/
void mutableStringMutableCopy()
{
NSMutableString *srcStr = [NSMutableString stringWithFormat:@"age is %d",10];
NSMutableString *copyStr = [srcStr mutableCopy];
[copyStr appendString:@"abc"];
NSLog(@"srcStr=%@,copyStr=%@",srcStr,copyStr);
}
/**
NSMutableString调用copy:深复制
*/
void mutableStringCopy()
{
NSMutableString *srcStr = [NSMutableString stringWithFormat@"age is %d",10];
NSString *copyStr = [srcStr copy];
[srcStr appendString:@"abc"];
NSLog(@"srcStr=%p, copyStr=%p", srcStr, copySt);
}
/**
NSString调用mutablecopy:深复制
*/
void stringMutableCopy()
{
NSString *srcStr = [NSString stringWithFormat:@"age is %d",10];
NSMutableString *copyStr = [srcStr mutableCopy];
[copyStr appendString:@"abc"];
NSLog(@"srcStr=%@, copyStr=%@", srcStr, copyStr);
}
/**
NSString调用copy:浅复制
*/
void stringCopy()
{
//copy:产生的肯定是不可变副本
//如果是不可变对象调用copy方法产出不可变副本,那么不会产生新的对象
NSString *srcStr = [NSString stringWithFormat:@"age is %d",10];
NSString *copyStr = [srcStr copy];
NSLog(@"%p %p",srcStr,copyStr);
}
```
文章属于摘抄搬砖!