iOS ARC 徹底指南

http://blog.csdn.net/zkdemon/article/details/7446385

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 的屬性定義,因此不須要給他們單獨的明示聲明瞭。
相關文章
相關標籤/搜索