天天看點

iOS中 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.     return [[retStr retain] autorelease];  
  5. }  

開啟ARC後,應改為:

  1. -(NSString *)stringTest  
  2. {  
  3.     __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];  
  4.     return retStr;  
  5. }  

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 

assign

instead.)

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

//-----------------------

http://blog.csdn.net/q199109106q/article/details/8565017

參考文獻:  iOS ARC 完全指南

提示:本文中所說的"執行個體變量"即是"成員變量","局部變量"即是"本地變量"

一、簡介

ARC是自iOS 5之後增加的新特性,完全消除了手動管理記憶體的煩瑣,編譯器會自動在适當的地方插入适當的retain、release、autorelease語句。你不再需要擔心記憶體管理,因為編譯器為你處理了一切

注意:ARC 是編譯器特性,而不是 iOS 運作時特性(除了weak指針系統),它也不是類似于其它語言中的垃圾收集器。是以 ARC 和手動記憶體管理性能是一樣的,有時還能更加快速,因為編譯器還可以執行某些優化

二、原理

ARC 的規則非常簡單:隻要還有一個變量指向對象,對象就會保持在記憶體中。當指針指向新值,或者指針不再存在時,相關聯的對象就會自動釋放。這條規則對于執行個體變量、synthesize屬性、局部變量都是适用的

三、strong指針

控制器中有個文本輸入框框屬性

[java]  view plain copy

  1. @property (nonatomic, assign) IBOutlet UITextField *nameField;  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

1.如果使用者在文本框中輸入mj這個字元串

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

那麼就可以說,nameField的text屬性是NSString對象的指針,也就是擁有者,該對象儲存了文本輸入框的内容

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

2.如果執行了如下代碼

[java]  view plain copy

  1. NSString *name = self.nameField.text;  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

一個對象可以有多個擁有者,在上面代碼中,name變量同樣也是這個NSString對象的擁有者,也就是有兩個指針指向同一個對象

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

3.随後使用者改變了輸入框的内容,比如

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

此時nameFeild的text屬性就指向了新的NSString對象。但原來的NSString對象仍然還有一個所有者(name變量),是以會繼續保留在記憶體中

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

4.當name變量獲得新值,或者不再存在時(如局部變量方法傳回時、執行個體變量對象釋放時),原先的NSString對象就不再擁有任何所有者,retain計數降為0,這時對象會被釋放

如,給name變量賦予一個新值

[java]  view plain copy

  1. name = @"Jake";  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

我們稱name和nameField.text指針為"Strong指針",因為它們能夠保持對象的生命。預設所有執行個體變量和局部變量都是Strong指針

四、weak指針

weak型的指針變量仍然可以指向一個對象,但不屬于對象的擁有者

1.執行下面的代碼

[java]  view plain copy

  1. __weak NSString *name = self.nameField.text;  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

name變量和nameField.text屬性都指向同一個NSString對象,但name不是擁有者

2.如果文本框的内容發生變化,則原先的NSString對象就沒有擁有者,會被釋放,此時name變量會自動變成nil,稱為空指針

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

weak型的指針變量自動變為nil是非常友善的,這樣阻止了weak指針繼續指向已釋放對象,避免了野指針的産生,不然會導緻非常難于尋找的Bug,空指針消除了類似的問題

3.weak指針主要用于“父-子”關系,父親擁有一個兒子的strong指針,是以父親是兒子的所有者;但為了阻止所有權循環,兒子需要使用weak指針指向父親。典型例子是delegate模式,你的ViewController通過strong指針(self.view)擁有一個UITableView, UITableView的dataSource和delegate都是weak指針,指向你的ViewController

iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

五、strong和weak指針的使用注意

1.下面代碼是有問題的:

[java]  view plain copy

  1. __weak NSString *str = [[NSString alloc] initWithFormat:@"1234"];  
  2. NSLog(@"%@", str); // 列印出來是"(null)"  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

str是個weak指針,是以NSString對象沒有擁有者,在建立之後就會被立即釋放。Xcode還會給出警告("Warning: Assigning retained object to weak variable; object will be released after assignment")

2.一般的指針變量預設就是strong類型的,是以一般我們對于strong變量不加__strong修飾,以下兩行代碼是等價的:

[java]  view plain copy

  1. NSString *name = self.nameField.text;  
  2. __strong NSString *name = self.nameField.text;  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

3.屬性可以是strong或weak,寫法如下

[java]  view plain copy

  1. @property (nonatomic, strong) NSString *name;  
  2. @property (nonatomic, weak) id delegate;  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

4. 以下代碼在ARC之前是可能會行不通的,因為在手動記憶體管理中,從NSArray中移除一個對象時,這個對象會發送一條release消息,可能會被立即釋放。随後NSLog()列印該對象就會導緻應用崩潰

[java]  view plain copy

  1. id obj = [array objectAtIndex:0];  
  2. [array removeObjectAtIndex:0];  
  3. NSLog(@"%@", obj);  
iOS中 property中的屬性strong 、weak、copy 、assign 、retain 、unsafe_unretained 與autoreleasing差別和作用詳解

在ARC中這段代碼是完全合法的,因為obj變量是一個strong指針,它成為了對象的擁有者,從NSArray中移除該對象也不會導緻對象被釋放

六、ARC小結

1.有了ARC,我們的代碼可以清晰很多,你不再需要考慮什麼時候retain或release對象。唯一需要考慮的是對象之間的關聯,也就是哪個對象擁有哪個對象?

2.ARC也有一些限制:

1> 首先ARC隻能工作于Objective-C對象,如果應用使用了Core Foundation或malloc()/free(),此時還是需要你來手動管理記憶體

2> 此外ARC還有其它一些更為嚴格的語言規則,以確定ARC能夠正常地工作

3.雖然ARC管理了retain和release,但并不表示你完全不需要關心記憶體管理的問題。因為strong指針會保持對象的生命,某些情況下你仍然需要手動設定這些指針為nil,否則可能導緻應用記憶體不足。無論何時你建立一個新對象時,都需要考慮誰擁有該對象,以及這個對象需要存活多久

4.ARC還能很好地結合C++使用,這對遊戲開發是非常有幫助的。對于iOS 4,ARC有一點點限制(不支援weak指針),但也沒太大關系

七、ARC使用注意總結

1.不能直接調用dealloc方法,不能調用retain,release,autorelease,retainCount方法,包括@selector(retain)的方式也不行 2.可以用dealloc方法來管理一些資源,但不能用來釋放執行個體變量,也不能在dealloc方法裡面去掉[super dealloc]方法,在ARC下父類的dealloc同樣由編譯器來自動完成 3.Core Foundation類型的對象仍然可以用CFRetain,CFRelease這些方法 4.不能再使用NSAllocateObject和NSDeallocateObject對象 5.不能在C結構體中使用對象指針,如果有類似功能可以建立一個Objective-C類來管理這些對象 6.在id和void*之間沒有簡便的轉換方法,同樣在Objective-C和Core Foundation類型之間的轉換都需要使用編譯器制定的轉換函數 7.不能再使用NSAutoreleasePool對象,ARC提供了@autoreleasepool塊來代替它,這樣更有效率 8.不能使用記憶體存儲區(不能再使用NSZone) 9.不能以new為開頭給一個屬性命名 10.聲明IBOutlet時一般應當使用weak,除了對StoryBoard這樣nib中間的頂層對象要用strong 11.weak相當于老版本的assign,strong相當于retain

繼續閱讀