Objective-C学习——copy, retain,assign,readonly,readwrite,strong,weak,nonatomic,unsafe_unretained

说明:看了很多书上的描述,对retain assign和copy的都是草草描述,没有谈及本质,但是retain assign和copy却是内存管理的重要内容,所以自己总结了下,本文涉及到了Objective-C内存管理的原则,阅读前需要了解。

retain assigncopy在@propeerty中的使用实际上是通过控制set方法进行内存管理。下面详细介绍:

1.retain:

   例如:

       @class Dog

       @property(nonatomic,retain) Dog *dog;

   注:nonatomic与atomic相对应,涉及线程,比较麻烦,这里不做赘述,读者仅需记住,nonatomic相对于atomic来说性能高,而声明属性时一般默认为atomic,故需在此申明nonatomic

这里使用了retain,那么在set方法中,究竟如何体现的呢?

- (void)setDog:(Dog*)dog

{

  if(_dog !=dog){         //判断是否需要重新赋值

     [_dogrelease];       //释放旧引用,计数器-1

     _dog = [dog retain];  //重新赋值,计数器+1

  }

}

2.assign:

   例如:

       @property(nonatomic,assign)int count;

这里使用了assign,那么在set方法中,究竟如何体现的呢?

-(void)setCount:(int)count

{

  _count = count;

}

2.assign:

   例如:

 

       @property(nonatomic,copy)NSString * str;

这里使用了copy,那么在set方法中,究竟如何体现的呢?

- (void)setStr:(NSString*)str

{

 if(_str !=str){         //判断是否需要重新赋值

     [_strrelease];       //释放旧引用,计数器-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等不可变的对象,因为是重新创建了对象,并且内容不变,因此不用担心后面的操作会对该属性的值产生影响。

 

copy与retain实例分析:

     假设str为对象p的属性

     @property(nonatomic,copy)NSString *str;

     NSMutableSting *s = [[NSMutableSting alloc]setString:@"hello"];

      p.str= s;  //此时,str的值为@"hello"

     [s appendString:@"world"];  //此时,s的值为"hello world",但是str的值依然为"hello".

    但是如果开始时str的申明为:

   @property(nonatomic,retain)NSString *str;

   那么,在进行完 [s appendString:@"world"]之后,str的值将变为"helloworld".因为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]; 
// name’s retain count has been bumped up by 1 

}


对参数进行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]; 
// name’s retain count has been bumped up by 1 

}
copy与retain:
Copy其实是建立了一个相同的对象,而retain不是:
比如一个NSString对象,地址为0×1111,内容为@”STR”
Copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象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协 议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是 可拷贝的。


assign与retain:
1. 接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。
2. 了解到1中assign的问题,那么如何解决?最简单的一个方法就是使用引用计数(reference counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。
总结:上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。

NSString *pt = [[NSString alloc] initWithString:@"abc"];
上面一段代码会执行以下两个动作
1 在堆上分配一段内存用来存储@"abc" 比如:内存地址为:0X1111 内容为 "abc"
2 在栈上分配一段内存用来存储pt 比如:地址为:0Xaaaa 内容自然为0X1111 
下面分别看下assign retain copy
assign的情况:NSString *newPt = [pt assing]; 
此时newPt和pt完全相同 地址都是0Xaaaa 内容为0X1111 即newPt只是pt的别名,对任何一个操作就等于对另一个操作。 因此retainCount不需要增加。
retain的情况:NSString *newPt = [pt retain]; 
此时newPt的地址不再为0Xaaaa,可能为0Xaabb 但是内容依然为0X1111。 因此newPt 和 pt 都可以管理"abc"所在的内存。因此 retainCount需要增加1
copy的情况:NSString *newPt = [pt copy];
此时会在堆上重新开辟一段内存存放@"abc" 比如0X1122 内容为@"abc 同时会在栈上为newPt分配空间 比如地址:0Xaacc 内容为0X1122 因此retainCount增加1供newPt来管理0X1122这段内存


readonly此标记说明属性是只读的,默认的标记是读写,如果你指定了只读,在@implementation中只需要一个读取器。或者如果你使用@synthesize关键字,也是有读取器方法被解析。而且如果你试图使用点操作符为属性赋值,你将得到一个编译错误。


readwrite此标记说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在@implementation中实现。如果使用@synthesize关键字,读取器和设置器都会被解析。


nonatomic:非原子性访问,对属性赋值的时候不加锁,多线程并发访问会提高性能。如果不加此属性,则默认是两个访问方法都为原子型事务访问。


以上来自http://hi.baidu.com/wolf_childer/item/3a0f1affb238a11ce2e3bdb0



weak and strong property (强引用和弱引用的区别)

 weak 和 strong 属性只有在你打开ARC时才会被要求使用,这时你是不能使用retain release autorelease 操作的,因为ARC会自动为你做好这些操作,但是你需要在对象属性上使用weak 和strong,其中strong就相当于retain属性,而weak相当于assign。

只有一种情况你需要使用weak(默认是strong),就是为了避免retain cycles(就是父类中含有子类{父类retain了子类},子类中又调用了父类{子类又retain了父类},这样都无法release)

ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 以后可以使用该特性。



property 中的strong 与weak


strong关键字与retain关似,用了它,引用计数自动+1,用实例更能说明一切

@property (nonatomic, strong) NSString *string1; 

@property (nonatomic, strong) NSString *string2; 


有这样两个属性,


@synthesize string1; 

@synthesize string2; 




猜一下下面代码将输出什么结果?


self.string1 = @"String 1"; 

self.string2 = self.string1; 

self.string1 = nil; 

NSLog(@"String 2 = %@", self.string2); 



结果是:String 2 = String 1

由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1", 如果你对retain熟悉的话,这理解并不难。


接着我们来看weak关键字:

如果这样声明两个属性:


@property (nonatomic, strong) NSString *string1; 

@property (nonatomic, weak) NSString *string2; 


并定义 

@synthesize string1; 

@synthesize string2; 


再来猜一下,下面输出是什么?


self.string1 = @"String 1"; 

self.string2 = self.string1; 

self.string1 = nil; 

NSLog(@"String 2 = %@", self.string2); 


结果是:String 2 = null


分析一下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步。

以上来自 http://www.cnblogs.com/mybkn/archive/2012/03/08/2384860.html


关于一些UI的property应该使用retain、strong还是weak还有其他一些问题

关于一些UI的property应该使用retain、strong还是weak。
1、在斯坦福大学的那个视频上看到的是说使用weak,ios5会帮你做剩余的一切,连release也不用了,dealloc都不用重载了(或许我没看那么认真,感觉好像是这样)。
2、在公司的项目代码上经常看到使用retain
3、使用@property进行了声明,还需要声明实例变量吗(在Interface下面打两个花括号那种)。在斯坦福大学的那个视频上好像没看到使用实例变量(目前看到第三个)。
4、目前流行的iphone4和iphone4s是使用ios4吗?那为了兼容,是否不应该使用ios5的新特性。


1、对于retain来说,一般指的是指针,这些属性需要保存引用计数,防止出现僵尸的情况,当时对于NSString类型,这个不是retain,而是copy,但是对于strong来说,这些你都不需要考虑,它会自己判断是选择retain还是copy,而对于assign来说,一是非指针变量,比如说NSInteger之类的,还有就是避免出现循环引用的时候,对于weak,其和assign差不多,但是它多了一点,就是,它会自动对该类型变量设置为nil。
2、至于公司经常使用retain的原因,一个是由于编码习惯,还有就是根据项目需要。
3、是否需要声明实例变量,这些都要看特殊情况的,假如在interface中你并没有声明实例变量,但是你在对于的.m文件中@synthesize的时候,那个时候其实就表明了你已经声明这个实例变量了。
4、对于这个问题,系统是可以升级的,并没有什么规定iphone4s只能使用IOS4。



1.具体一点:IBOutlet可以为weak,NSString为copy,Delegate一般为weak,其他的看情况。一般来说,类“内部”的属性设置为strong,类“外部”的属性设置为weak。说到底就是一个归属权的问题。小心出现循环引用导致内存无法释放。
2.不用ARC的话就会看到很多retian。
3.如果你写了@synthesize abc = _abc;的话,系统自动帮你声明了一个_abc的实例变量。


  使用assign: 对基础数据类型 (NSInteger)和C数据类型(int, float, double, char,等)
  使用copy: 对NSString 
  使用retain: 对其他NSObject和其子类 


来自  http://www.cocoachina.com/bbs/simple/?t110450.html



property中的strong 、weak、copy 、assign 、retain 、unsafe_unretained 与autoreleasing区别和作用详解


iOS5中加入了新知识,就是ARC,其实我并不是很喜欢它,因为习惯了自己管理内存。但是学习还是很有必要的。

在iOS开发过程中,属性的定义往往与retain, assign, copy有关,我想大家都很熟悉了,在此我也不介绍,网上有很多相关文章。

现在我们看看iOS5中新的关键字strong, weak, unsafe_unretained. 可以与以前的关键字对应学习strong与retain类似,weak与unsafe_unretained功能差不多(有点区别,等下会介绍,这两个新关键字与assign类似)。在iOS5中用这些新的关键字,就可以不用手动管理内存了,从java等其它语言转过来的程序员非常受用。


strong关键字与retain关似,用了它,引用计数自动+1,用实例更能说明一切

  1. @property (nonatomic, strong) NSString *string1;   
  2. @property (nonatomic, strong) NSString *string2;  

有这样两个属性,

  1. @synthesize string1;   
  2. @synthesize string2;  


猜一下下面代码将输出什么结果?


  1. self.string1 = @"String 1";   
  2. self.string2 = self.string1;   
  3. self.string1 = nil;  
  4. NSLog(@"String 2 = %@", self.string2);  


结果是:String 2 = String 1

由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1", 如果你对retain熟悉的话,这理解并不难。


接着我们来看weak关键字:

如果这样声明两个属性:

  1. @property (nonatomic, strong) NSString *string1;   
  2. @property (nonatomic, weak) NSString *string2;  

并定义
  1. @synthesize string1;   
  2. @synthesize string2;  


再来猜一下,下面输出是什么?
  1. self.string1 = @"String 1";   
  2. self.string2 = self.string1;   
  3. self.string1 = nil;  
  4. NSLog(@"String 2 = %@", self.string2);  

结果是:String 2 = null

分析一下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。在c/c++开发过程中,为何大牛都说指针的空间释放了后,都要将指针赋为NULL. 在这儿用weak关键字帮我们做了这一步。


接着我们来看unsafe_unretained

从名字可以看出,unretained且unsafe,由于是unretained所以与weak有点类似,但是它是unsafe的,什么是unsafe的呢,下面看实例。

如果这样声明两个属性:

并定义

  1. @property (nonatomic, strong) NSString *string1;   
  2. @property (nonatomic, unsafe_unretained) NSString *string2;  

再来猜一下,下面的代码会有什么结果?
  1. self.string1 = @"String 1";   
  2. self.string2 = self.string1;   
  3. self.string1 = nil;  
  4. NSLog(@"String 2 = %@", self.string2);  

请注意,在此我并没有叫你猜会有什么输出,因为根本不会有输出,你的程序会crash掉。

原因是什么,其实就是野指针造成的,所以野指针是可怕的。为何会造成野指针呢?同于用unsafe_unretained声明的指针,由于self.string1=nil已将内存释放掉了,但是string2并不知道已被释放了,所以是野指针。然后访问野指针的内存就造成crash.  所以尽量少用unsafe_unretained关键字。


strong,weak, unsafe_unretained往往都是用来声明属性的,如果想声明临时变量就得用__strong,  __weak, __unsafe_unretained,  __autoreleasing, 其用法与上面介绍的类似。

还是看看实例吧。

  1. __strong NSString *yourString = @"Your String";   
  2. __weak  NSString *myString = yourString;   
  3. yourString = nil;   
  4. __unsafe_unretained NSString *theirString = myString;  
  5. //现在所有的指针都为nil  

再看一个:

  1. __strong NSString *yourString = @"Your String";   
  2. __weak  NSString *myString = yourString;   
  3. __unsafe_unretained NSString *theirString = myString;  
  4. yourString = nil;   
  5. //现在yourString与myString的指针都为nil,而theirString不为nil,但是是野指针。  

__autoreleasing的用法介绍:

在c/c++,objective-c内存管理中有一条是:谁分配谁释放。 __autoreleasing则可以使对像延迟释放。比如你想传一个未初始化地对像引用到一个方法当中,在此方法中实始化此对像,那么这种情况将是__autoreleasing表演的时候。看个示例:

  1. - (void) generateErrorInVariable:(__autoreleasing NSError **)paramError{   
  2.     NSArray *objects = [[NSArray alloc] initWithObjects:@"A simple error", nil];  
  3.     NSArray *keys = [[NSArray alloc] initWithObjects:NSLocalizedDescriptionKey, nil];  
  4.     NSDictionary *errorDictionary = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];  
  5.     *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];  
  6. }  
  7. -(void)test  
  8. {  
  9.     NSError *error = nil;   
  10.     [self generateErrorInVariable:&error];  
  11.     NSLog(@"Error = %@", error);  
  12. }  

这样即便在函数内部申请的空间,在函数外部也可以使用,同样也适合谁分配谁释放的原则。

同样下面的代码也是类似原因, 只不过在没有开启ARC的情况下适用:

  1. -(NSString *)stringTest  
  2. {  
  3.     NSString *retStr = [NSString stringWithString:@"test"];  
  4.       
  5.     return [[retStr retain] autorelease];  
  6. }  

开启ARC后,应改为:
  1. -(NSString *)stringTest  
  2. {  
  3.     __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];  
  4.       
  5.     return retStr;  
  6. }  

 

Setter Semantics

 

These attributes specify the semantics of a set accessor. They are mutually exclusive.

 

strong

Specifies that there is a strong (owning) relationship to the destination object.

weak

Specifies that there is a weak (non-owning) relationship to the destination object.

If the destination object is deallocated, the property value is automatically set to nil.

(Weak properties are not supported on OS X v10.6 and iOS 4; use assigninstead.)

copy

Specifies that a copy of the object should be used for assignment.

The previous value is sent a release message.

The copy is made by invoking the copy method. This attribute is valid only for object types, which must implement the NSCopying protocol.

assign

Specifies that the setter uses simple assignment. This attribute is the default.

You use this attribute for scalar types such as NSInteger and CGRect.

retain

Specifies that retain should be invoked on the object upon assignment.

The previous value is sent a release message.

In OS X v10.6 and later, you can use the __attribute__ keyword to specify that a Core Foundation property should be treated like an Objective-C object for memory management:

@property(retain) __attribute__((NSObject)) CFDictionaryRef myDictionary;

 http://www.33lc.com/article/661.html


你可能感兴趣的:(iOS)