ARC簡介以及工程中ARC與非ARC的混合
ARC與非ARC在一個專案中同時使用,
1,選擇專案中的Targets,選中你所要操作的Target,
2,選Build Phases,在其中Complie Sources中選擇需要ARC的檔案雙擊,並在輸入框中輸入:-fobjc-arc,如果不要ARC則輸入:-fno-objc-arc
混用沒有問題,沒有用ARC的程式碼繼續堅持誰申請誰釋放就好了。以前的庫沒有時間重寫,都採用這種方法。
而且不知道你用的是什麼第三方程式碼,一般來說,現在很少有arc only的程式碼,大部分都是用一些巨集來讓程式碼可以同時適應arc和非arc的(用#if __has_feature(objc_arc)判斷)。如果程式碼量不大,可以考慮自己進行改寫
ARC是什麼
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是編譯器LLVM 3.0的
你不能使用new開頭的變數
ARC只對objective-c物件起作用,對於Core Foundation 之類,你仍然需要自己手動釋放。
ARC的優勢:
1、不再需要考慮retain、release的問題,我們得以在更高的層面考慮問題,而不是糾結於什麼時候釋放的細節。
2、zeroing-weak reference , 我們終於可以擺脫煩人的
3、ARC給人的感覺是直接存物件到變數裡面。因此之前一段不可能的程式碼現在也變為可能:
變化點
通過一小段程式碼,我們看看使用ARC前後的變化點。
@interface NonARCObject : NSObject { NSString *name; } -(id)initWithName:(NSString *)name; @end @implementation NonARCObject -(id)initWithName:(NSString *)newName { self = [super init]; if (self) { name = [newName retain]; } return self; } -(void)dealloc { [name release]; [Super dealloc]; } @end
@interface ARCObject : NSObject { NSString *name; } -(id)initWithName:(NSString *)name; @end @implementation ARCObject -(id)initWithName:(NSString *)newName { self = [super init]; if (self) { name = newName; } return self; } @end
- 我們之前使用Objective-C中記憶體管理規則時,往往採用下面的準則
生成物件時,使用autorelease
物件代入時,先autorelease後再retain
物件在函式中返回時,使用return [[object retain] autorelease];
而使用ARC後,我們可以不需要這樣做了,甚至連最基礎的release都不需要了。
使用ARC的好處
使用ARC有什麼好處呢?
看到上面的例子,大家就知道了,以後寫Objective-C的程式碼變得簡單多了,因為我們不需要擔心煩人的記憶體管理,擔心記憶體洩露了
程式碼的總量變少了,看上去清爽了不少,也節省了勞動力
程式碼高速化,由於使用編譯器管理引用計數,減少了低效程式碼的可能性
不好的地方
記住一堆新的ARC規則 — 關鍵字及特性等需要一定的學習週期
一些舊的程式碼,第三方程式碼使用的時候比較麻煩;修改程式碼需要工數,要麼修改編譯開關
關於第二點,由於 XCode4.2 中預設ARC就是 ON 的狀態,所以編譯舊程式碼的時候往往有"Automatic Reference Counting Issue"的錯誤資訊。
這個時候,可以將專案編譯設定中的“Objectice-C Auto Reference Counteting”設為NO。如下所示。
如果只想對某個.m檔案不適應ARC,可以只針對該類檔案加上 -fno-objc-arc 編譯FLAGS,如下圖。
ARC基本規則
retain, release, autorelease, dealloc由編譯器自動插入,不能在程式碼中呼叫
dealloc雖然可以被過載,但是不能呼叫[super dealloc]
由於ARC並不是GC,並需要一些規則讓編譯器支援程式碼插入,所以必須清楚清楚了這些規則後,才能寫出健壯的程式碼。
Objective-C物件
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)
(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 開始匯入。由於該型別不影響物件的生命週期,所以如果物件之前就沒有持有者,那麼會出現剛建立就被破棄的問題,比如下面的程式碼。
NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; 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。他被經常用於函式有值引數返回時的處理,比如下面的例子。
- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError { .... *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary]; } .... { NSError *error = nil; [self generateErrorInVariable:&error]; NSLog(@"Error = %@", error); }
又如函式的返回值是在函式中申請的,那麼希望釋放是在呼叫端時,往往有下面的程式碼。
-(NSString *)stringTest { NSString *retStr = [NSString stringWithString:@"test"]; return [[retStr retain] autorelease]; }
// 使用ARC -(NSString *)stringTest { __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"]; return retStr; }
即當方法的引數是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”)