iOS應(yīng)用開發(fā):什么是ARC?
新年伊始,萬象更新。新一年開始,我們來更加深入了解一下iPhone開發(fā)的內(nèi)部。作為開始,我們先來了解一下ARC。
ARC是iOS 5推出的新功能,全稱叫 ARC(Automatic Reference Counting)。簡單地說,就是代碼中自動(dòng)加入了retain/release,原先需要手動(dòng)添加的用來處理內(nèi)存管理的引用計(jì)數(shù)的代碼可以自動(dòng)地由編譯器完成了。
該機(jī)能在 iOS 5/ Mac OS X 10.7 開始導(dǎo)入,利用 Xcode4.2 可以使用該機(jī)能。簡單地理解ARC,就是通過指定的語法,讓編譯器(LLVM 3.0)在編譯代碼時(shí),自動(dòng)生成實(shí)例的引用計(jì)數(shù)管理部分代碼。有一點(diǎn),ARC并不是GC,它只是一種代碼靜態(tài)分析(Static Analyzer)工具。
通過一小段代碼,我們看看使用ARC前后的變化點(diǎn)。
- @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
 
- 生成對象時(shí),使用autorelease
 - 對象代入時(shí),先autorelease后再retain
 - 對象在函數(shù)中返回時(shí),使用return [[object retain] autorelease];
 
而使用ARC后,我們可以不需要這樣做了,甚至連最基礎(chǔ)的release都不需要了。
使用ARC有什么好處呢?
- 看到上面的例子,大家就知道了,以后寫Objective-C的代碼變得簡單多了,因?yàn)槲覀儾恍枰獡?dān)心煩人的內(nèi)存管理,擔(dān)心內(nèi)存泄露了
 - 代碼的總量變少了,看上去清爽了不少,也節(jié)省了勞動(dòng)力
 - 代碼高速化,由于使用編譯器管理引用計(jì)數(shù),減少了低效代碼的可能性
 
- 記住一堆新的ARC規(guī)則 — 關(guān)鍵字及特性等需要一定的學(xué)習(xí)周期
 - 一些舊的代碼,第三方代碼使用的時(shí)候比較麻煩;修改代碼需要工數(shù),要么修改編譯開關(guān)
 
關(guān)于第二點(diǎn),由于 XCode4.2 中缺省ARC就是 ON 的狀態(tài),所以編譯舊代碼的時(shí)候往往有"Automatic Reference Counting Issue"的錯(cuò)誤信息。

這個(gè)時(shí)候,可以將項(xiàng)目編譯設(shè)置中的“Objectice-C Auto Reference Counteting”設(shè)為NO。如下所示。

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

- retain, release, autorelease, dealloc由編譯器自動(dòng)插入,不能在代碼中調(diào)用
 - dealloc雖然可以被重載,但是不能調(diào)用[super dealloc]
 
由于ARC并不是GC,并需要一些規(guī)則讓編譯器支持代碼插入,所以必須清楚清楚了這些規(guī)則后,才能寫出健壯的代碼。
ObjectiveC中的對象,有強(qiáng)參照(Strong reference)和弱參照(Weak reference)之分,當(dāng)需要保持其他對象的時(shí)候,需要retain以確保對象引用計(jì)數(shù)加1。對象的持有者(owner)只要存在,那么該對象的強(qiáng)參照就一直存在。
- 對象處理的基本規(guī)則是
 
- 只要對象的持有者存在(對象被強(qiáng)參照),那么就可以使用該對象
 - 對象失去了持有者后,即被破棄
 
強(qiáng)參照 (Strong reference)
(s1)
firstName作為”natsu”字符串對象的最初持有者,是該NSString類型對象的Strong reference。
- (s2)
 
這里將firstName代入到aName中,即aName也成為了@”natsu”字符串對象的持有者,對于該對象,aName也是Strong reference。
- (s3)
 
這里,改變firstName的內(nèi)容。生成新的字符串對象”maki”。這時(shí)候firstName成為”maki”的持有者,而@”natsu”的持有者只有aName。每個(gè)字符串對象都有各自的持有者,所以它們都在內(nèi)存中都存在。
- (s4)
 
追加新的變量otherName, 它將成為@”maki”對象的另一個(gè)持有者。即NSString類型對象的Strong reference。
- (s5)
 
將otherName代入到aName,這時(shí),aName將成為@”maki”字符串對象的持有者。而對象@”natsu”已經(jīng)沒有持有者了,該對象將被破棄。
弱參照 (Weak reference)
接下來我們來看看弱參照 (Weak reference) 的使用方式。

(w1)
與強(qiáng)參照方式同樣,firstName作為字符串對象@”natsu”的持有者存在。即是該NSString類型對象的Strong reference。
- (w2)
 
使用關(guān)鍵字__weak,聲明弱參照weakName變量,將firstName代入。這時(shí)weakName雖然參照@”natsu”,但仍是Weak reference。即weakName雖然能看到@”natsu”,但不是其持有者。
- (w3)
 
firstName指向了新的對象@”maki”,成為其持有者,而對象@”natsu”因?yàn)闆]有了持有者,即被破棄。同時(shí)weakName變量將被自動(dòng)代入nil。
ARC中關(guān)于對象的引用參照,主要有下面幾關(guān)鍵字。使用strong, weak, autoreleasing限定的變量會(huì)被隱式初始化為nil。
- __strong
 
變量聲明缺省都帶有__strong關(guān)鍵字,如果變量什么關(guān)鍵字都不寫,那么缺省就是強(qiáng)參照。
- __weak
 
上面已經(jīng)看到了,這是弱參照的關(guān)鍵字。該概念是新特性,從 iOS 5/ Mac OS X 10.7 開始導(dǎo)入。由于該類型不影響對象的生命周期,所以如果對象之前就沒有持有者,那么會(huì)出現(xiàn)剛創(chuàng)建就被破棄的問題,比如下面的代碼。
- NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]];
 - NSLog(@"string: %@", string); //此時(shí) string為空
 
如果編譯設(shè)定OS版本 Deployment Target 設(shè)定為這比這低的版本,那么編譯時(shí)將報(bào)錯(cuò)(The current deployment target does not support automated __weak references),這個(gè)時(shí)候,我們可以使用下面的__unsafe_unretained。
弱參照還有一個(gè)特征,即當(dāng)參數(shù)對象失去所有者之后,變量會(huì)被自動(dòng)付上nil (Zeroing)。
- __unsafe_unretained
 
該關(guān)鍵字與__weak一樣,也是弱參照,與__weak的區(qū)別只是是否執(zhí)行nil賦值(Zeroing)。但是這樣,需要注意變量所指的對象已經(jīng)被破棄了,地址還還存在,但內(nèi)存中對象已經(jīng)沒有了。如果還是訪問該對象,將引起「BAD_ACCESS」錯(cuò)誤。
- __autoreleasing
 
該關(guān)鍵字使對像延遲釋放。比如你想傳一個(gè)未初始化的對像引用到一個(gè)方法當(dāng)中,在此方法中實(shí)例化此對像,那么這種情況可以使用__autoreleasing。他被經(jīng)常用于函數(shù)有值參數(shù)返回時(shí)的處理,比如下面的例子。
- - (void) generateErrorInVariable:(__autoreleasing NSError **)paramError {
 - ....
 - *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary];
 - }
 - ....
 - {
 - NSError *error = nil;
 - [self generateErrorInVariable:&error];
 - NSLog(@"Error = %@", error);
 - }
 
又如函數(shù)的返回值是在函數(shù)中申請的,那么希望釋放是在調(diào)用端時(shí),往往有下面的代碼。
- -(NSString *)stringTest
 - {
 - NSString *retStr = [NSString stringWithString:@"test"];
 - return [[retStr retain] autorelease];
 - }
 - // 使用ARC
 - -(NSString *)stringTest
 - {
 - __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"];
 - return retStr;
 - }
 
即當(dāng)方法的參數(shù)是id*,且希望方法返回時(shí)對象被autoreleased,那么使用該關(guān)鍵字。
- 今天,我們看到了基本的ARC使用規(guī)則
 
- 代碼中不能使用retain, release, retain, autorelease
 - 不重載dealloc(如果是釋放對象內(nèi)存以外的處理,是可以重載該函數(shù)的,但是不能調(diào)用[super dealloc])
 - 不能使用NSAllocateObject, NSDeallocateObject
 - 不能在C結(jié)構(gòu)體中使用對象指針
 - id與void *間的如果cast時(shí)需要用特定的方法(__bridge關(guān)鍵字)
 - 不能使用NSAutoReleasePool、而需要@autoreleasepool塊
 - 不能使用“new”開始的屬性名稱 (如果使用會(huì)有下面的編譯錯(cuò)誤”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”)
 
原文地址:http://www.yifeiyang.net/development-of-the-iphone-simply-1/















 
 
 

 
 
 
 