關於ARC需要了解的一些基本知識點

ARC指南:http://download.csdn.net/detail/zkdemon/4213790

ARC是iOS 5推出的新功能,全稱叫 ARC(Automatic Reference Counting)。簡單地說,就是代碼中自動加入了retain/release,原先需要手動添加的用來處理內存管理的引用計數的代碼可以自動地由編譯器完成了。

該機能在 iOS 5/ Mac OS X 10.7 開始導入,利用 Xcode4.2 可以使用該機能。簡單地理解ARC,就是通過指定的語法,讓編譯器(LLVM 3.0)在編譯代碼時,自動生成實例的引用計數管理部分代碼。有一點,ARC並不是GC,它只是一種代碼靜態分析(Static Analyzer)工具。

通過一小段代碼,我們看看使用ARC前後的變化點。

 

  1. @interface NonARCObject : NSObject {    
  2.     NSString *name;    
  3. }    
  4. -(id)initWithName:(NSString *)name;    
  5. @end    
  6.    
  7. @implementation NonARCObject    
  8. -(id)initWithName:(NSString *)newName {    
  9.     self = [super init];    
  10.     if (self) {    
  11.         name = [newName retain];    
  12.     }    
  13.     return self;    
  14. }    
  15.    
  16. -(void)dealloc {    
  17.     [name release];    
  18.     [Super dealloc];    
  19. }    
  20. @end    
 

 

  1. @interface ARCObject : NSObject {    
  2.     NSString *name;    
  3. }    
  4. -(id)initWithName:(NSString *)name;    
  5. @end    
  6.    
  7. @implementation ARCObject    
  8. -(id)initWithName:(NSString *)newName {    
  9.     self = [super init];    
  10.     if (self) {    
  11.         name = newName;    
  12.     }    
  13.     return self;    
  14. }    
  15. @end    
 

 

我們之前使用Objective-C中內存管理規則時,往往採用下面的準則
  •    生成對象時,使用autorelease
  •    對象代入時,先autorelease後再retain
  •    對象在函數中返回時,使用return [[object retain] autorelease];

而使用ARC後,我們可以不需要這樣做了,甚至連最基礎的release都不需要了。

 

使用ARC有什麼好處呢?

  •    看到上面的例子,大家就知道了,以後寫Objective-C的代碼變得簡單多了,因爲我們不需要擔心煩人的內存管理,擔心內存泄露了
  •    代碼的總量變少了,看上去清爽了不少,也節省了勞動力
  •    代碼高速化,由於使用編譯器管理引用計數,減少了低效代碼的可能性
  •    記住一堆新的ARC規則 — 關鍵字及特性等需要一定的學習週期
  •    一些舊的代碼,第三方代碼使用的時候比較麻煩;修改代碼需要工數,要麼修改編譯開關

關於第二點,由於 XCode4.2 中缺省ARC就是 ON 的狀態,所以編譯舊代碼的時候往往有"Automatic Reference Counting Issue"的錯誤信息。

 


這個時候,可以將項目編譯設置中的“Objectice-C Auto Reference Counteting”設爲NO。如下所示。

 


如果只想對某個.m文件不適應ARC,可以只針對該類文件加上 -fno-objc-arc 編譯FLAGS,如下圖。

 

 

  •     retain, release, autorelease, dealloc由編譯器自動插入,不能在代碼中調用
  •     dealloc雖然可以被重載,但是不能調用[super dealloc]

由於ARC並不是GC,並需要一些規則讓編譯器支持代碼插入,所以必須清楚清楚了這些規則後,才能寫出健壯的代碼。

 

ObjectiveC中的對象,有強參照(Strong reference)和弱參照(Weak reference)之分,當需要保持其他對象的時候,需要retain以確保對象引用計數加1。對象的持有者(owner)只要存在,那麼該對象的強參照就一直存在。

對象處理的基本規則是
  •     只要對象的持有者存在(對象被強參照),那麼就可以使用該對象
  •     對象失去了持有者後,即被破棄

 

強參照 (Strong reference)

 


(s1)

firstName作爲”natsu”字符串對象的最初持有者,是該NSString類型對象的Strong reference。

(s2)

這裏將firstName代入到aName中,即aName也成爲了@”natsu”字符串對象的持有者,對於該對象,aName也是Strong reference。

(s3)

這裏,改變firstName的內容。生成新的字符串對象”maki”。這時候firstName成爲”maki”的持有者,而@”natsu”的持有者只有aName。每個字符串對象都有各自的持有者,所以它們都在內存中都存在。

(s4)

追加新的變量otherName, 它將成爲@”maki”對象的另一個持有者。即NSString類型對象的Strong reference。

(s5)

將otherName代入到aName,這時,aName將成爲@”maki”字符串對象的持有者。而對象@”natsu”已經沒有持有者了,該對象將被破棄。

 

弱參照 (Weak reference)

接下來我們來看看弱參照 (Weak reference) 的使用方式。

 

 

(w1)

與強參照方式同樣,firstName作爲字符串對象@”natsu”的持有者存在。即是該NSString類型對象的Strong reference。

(w2)

使用關鍵字__weak,聲明弱參照weakName變量,將firstName代入。這時weakName雖然參照@”natsu”,但仍是Weak reference。即weakName雖然能看到@”natsu”,但不是其持有者。

(w3)

firstName指向了新的對象@”maki”,成爲其持有者,而對象@”natsu”因爲沒有了持有者,即被破棄。同時weakName變量將被自動代入nil。

 

ARC中關於對象的引用參照,主要有下面幾關鍵字。使用strong, weak, autoreleasing限定的變量會被隱式初始化爲nil。

 

  • __strong

變量聲明缺省都帶有__strong關鍵字,如果變量什麼關鍵字都不寫,那麼缺省就是強參照。

 

  • __weak

上面已經看到了,這是弱參照的關鍵字。該概念是新特性,從 iOS 5/ Mac OS X 10.7 開始導入。由於該類型不影響對象的生命週期,所以如果對象之前就沒有持有者,那麼會出現剛創建就被破棄的問題,比如下面的代碼。

 

  1. NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];    
  2. NSLog(@"string: %@", string); //此時 string爲空   

 

如果編譯設定OS版本 Deployment Target 設定爲這比這低的版本,那麼編譯時將報錯(The current deployment target does not support automated __weak references),這個時候,我們可以使用下面的 __unsafe_unretained。

弱參照還有一個特徵,即當參數對象失去所有者之後,變量會被自動付上nil (Zeroing)。

 

  • __unsafe_unretained

該關鍵字與__weak一樣,也是弱參照,與__weak的區別只是是否執行nil賦值(Zeroing)。但是這樣,需要注意變量所指的對象已經被破棄了,地址還還存在,但內存中對象已經沒有了。如果還是訪問該對象,將引起「BAD_ACCESS」錯誤。

 

  • __autoreleasing

該關鍵字使對像延遲釋放。比如你想傳一個未初始化的對像引用到一個方法當中,在此方法中實例化此對像,那麼這種情況可以使用__autoreleasing。他被經常用於函數有值參數返回時的處理,比如下面的例子。

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

又如函數的返回值是在函數中申請的,那麼希望釋放是在調用端時,往往有下面的代碼。

 

  1. -(NSString *)stringTest    
  2. {    
  3.     NSString *retStr = [NSString stringWithString:@"test"];    
  4.    
  5.     return [[retStr retain] autorelease];    
  6. }    
  7.    
  8. // 使用ARC    
  9.    
  10. -(NSString *)stringTest    
  11. {    
  12.     __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];    
  13.    
  14.     return retStr;    
  15. }    
 

 

即當方法的參數是id*,且希望方法返回時對象被autoreleased,那麼使用該關鍵字。

 

基本的ARC使用規則
  •     代碼中不能使用retain, release, retain, autorelease
  •     不重載dealloc(如果是釋放對象內存以外的處理,是可以重載該函數的,但是不能調用[super dealloc])
  •     不能使用NSAllocateObject, NSDeallocateObject
  •     不能在C結構體中使用對象指針
  •     id與void *間的如果cast時需要用特定的方法(__bridge關鍵字)
  •     不能使用NSAutoReleasePool、而需要@autoreleasepool塊
  •     不能使用“new”開始的屬性名稱 (如果使用會有下面的編譯錯誤”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”)
--------------------------------------------------------------------華麗的分割線--------------------------------------------------------------------

ARC工作原理是在編譯程序的時候由xCode將內存操作的代碼(如:retain,release 和 autorelease)自動添加到需要的位置。

ARC 只能在iOS4 和iOS5上使用,weak refrences 只能在iOS5上使用,並且只能是工程在ARC管理內存的時候才能用。
老版本的工程是可以轉換成使用ARC的工程,轉換規則包括:

        1.去掉所有的retain,release,autorelease

        2.把NSAutoRelease替換成@autoreleasepool{}塊

        3.把assign的屬性變爲weak

使用ARC的一些強制規定

        1.不能直接調用dealloc方法,不能調用retain,release,autorelease,reraubCount方法,包括@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.聲明outlet時一般應當使用weak,除了對StoryBoard 這樣nib中間的頂層對象要用strong

        11.weak 相當於老版本的assign,strong相當於retain

對工程中的單個文件制定不使用ARC的方法:在targets的build phases選項下Compile Sources下選擇要不使用arc編譯的文件,雙擊它,輸入-fno-objc-arc即可


屬性值 關鍵字 所有權
strong __strong
weak __weak
unsafe_unretained __unsafe_unretained
copy __strong
assign __unsafe_unretained
retain __strong

strong

該屬性值對應 __strong 關鍵字,即該屬性所聲明的變量將成爲對象的持有者。

weak

該屬性對應 __weak 關鍵字,與 __weak 定義的變量一致,該屬性所聲明的變量將沒有對象的所有權,並且當對象被破棄之後,對象將被自動賦值nil。

並且,delegate 和 Outlet 應該用 weak 屬性來聲明。同時,如上一回介紹的 iOS 5 之前的版本是沒有 __weak 關鍵字的,所以 weak 屬性是不能使用的。這種情況我們使用 unsafe_unretained。

unsafe_unretained

等效於__unsafe_unretaind關鍵字聲明的變量;像上面說明的,iOS 5之前的系統用該屬性代替 weak 來使用。

copy

與 strong 的區別是聲明變量是拷貝對象的持有者。

assign

一般Scalar Varible用該屬性聲明,比如,int, BOOL。

retain

該屬性與 strong 一致;只是可讀性更強一些。

讀寫相關的屬性 (readwrite, readonly)

讀寫相關的屬性有 readwrite 和 readonly 兩種,如果使用ARC之後,我麼需要注意一下 readonly 屬性的使用。

比如下面的變量聲明。

  1. @property (nonatomic, readonly) NSString *name;  

一般聲明爲 readonly 的變量按理說應該不需要持有所有權了,但是在ARC有效的情況下,將出現下面的錯誤信息 :

 “ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute
如果定義了ARC有效,那麼必須要有所有者屬性的定義;所以我們的代碼改成這樣,就OK了

  1. @property (nonatomic, strong, readonly) NSString *name; 

不過有一點,Scalar Varible的變量缺省都有 assign 的屬性定義,所以不需要給他們單獨的明示聲明瞭。

發佈了0 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章