本文來源於我個人的ARC學習筆記,旨在通過簡明扼要的方式總結出iOS開發中ARC(Automatic Reference Counting,自動引用計數)記憶體管理技術的要點,所以不會涉及全部細節。這篇文章不是一篇標準的ARC使用教程,並假定讀者已經對ARC有了一定了解和使用經驗。詳細的關於ARC的資訊請參見蘋果的官方文件與網上的其他教程:)
本文的主要內容:
- ARC的本質
- ARC的開啟與關閉
- ARC的修飾符
- ARC與Block
- ARC與Toll-Free Bridging
ARC的本質
ARC是編譯器(時)特性,而不是執行時特性,更不是垃圾回收器(GC)。
Automatic Reference Counting (ARC) is a compiler-level feature that simplifies the process of managing object lifetimes (memory management) in Cocoa applications.
ARC只是相對於MRC(Manual Reference Counting或稱為非ARC,下文中我們會一直使用MRC來指代非ARC的管理方式)的一次改進,但它和之前的技術本質上沒有區別。具體資訊可以參考ARC編譯器官方文件。
ARC的開啟與關閉
不同於XCode4可以在建立工程時選擇關閉ARC,XCode5在建立的工程是預設開啟ARC,沒有可以關閉ARC的選項。
如果需要對特定檔案開啟或關閉ARC,可以在工程選項中選擇Targets -> Compile Phases -> Compile Sources,在裡面找到對應檔案,新增flag:
- 開啟ARC:-fobjc-arc
- 關閉ARC:-fno-objc-arc
如圖:
ARC的修飾符
ARC主要提供了4種修飾符,他們分別是:__strong,__weak,__autoreleasing,__unsafe_unretained。
__strong
表示引用為強引用。對應在定義property時的”strong”。所有物件只有當沒有任何一個強引用指向時,才會被釋放。
注意:如果在宣告引用時不加修飾符,那麼引用將預設是強引用。當需要釋放強引用指向的物件時,需要將強引用置nil。
__weak
表示引用為弱引用。對應在定義property時用的”weak”。弱引用不會影響物件的釋放,即只要物件沒有任何強引用指向,即使有100個弱引用物件指向也沒用,該物件依然會被釋放。不過好在,物件在被釋放的同時,指向它的弱引用會自動被置nil,這個技術叫zeroing weak pointer。這樣有效得防止無效指標、野指標的產生。__weak一般用在delegate關係中防止迴圈引用或者用來修飾指向由Interface Builder編輯與生成的UI控制元件。
__autoreleasing
表示在autorelease pool中自動釋放物件的引用,和MRC時代autorelease的用法相同。定義property時不能使用這個修飾符,任何一個物件的property都不應該是autorelease型的。
一個常見的誤解是,在ARC中沒有autorelease,因為這樣一個“自動釋放”看起來好像有點多餘。這個誤解可能源自於將ARC的“自動”和autorelease“自動”的混淆。其實你只要看一下每個iOS App的main.m檔案就能知道,autorelease不僅好好的存在著,並且變得更fashion了:不需要再手工被建立,也不需要再顯式得呼叫[drain]方法釋放記憶體池。
以下兩行程式碼的意義是相同的。
1 2 |
NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC NSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC |
這裡關於autoreleasepool就不做展開了,詳細地資訊可以參考官方文件或者其他文章。
__autoreleasing在ARC中主要用在引數傳遞返回值(out-parameters)和引用傳遞引數(pass-by-reference)的情況下。
__autoreleasing
is used to denote arguments that are passed by reference (id *
) and are autoreleased on return.
比如常用的NSError的使用:
1 2 3 4 5 |
NSError *__autoreleasing error; if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) { NSLog(@"Error: %@", error); } |
(在上面的writeToFile方法中error引數的型別為(NSError *__autoreleasing *))
注意,如果你的error定義為了strong型,那麼,編譯器會幫你隱式地做如下事情,保證最終傳入函式的引數依然是個__autoreleasing型別的引用。
1 2 3 4 5 6 7 |
NSError *error; NSError *__autoreleasing tempError = error; // 編譯器新增 if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) { error = tempError; // 編譯器新增 NSLog(@"Error: %@", error); } |
所以為了提高效率,避免這種情況,我們一般在定義error的時候將其(老老實實地=。=)宣告為__autoreleasing型別的:
1 |
NSError *__autoreleasing error; |
在這裡,加上__autoreleasing之後,相當於在MRC中對返回值error做了如下事情:
1 |
*error = [[[NSError alloc] init] autorelease]; |
*error指向的物件在建立出來後,被放入到了autoreleasing pool中,等待使用結束後的自動釋放,函式外error的使用者並不需要關心*error指向物件的釋放。
另外一點,在ARC中,所有這種指標的指標 (NSError **)的函式引數如果不加修飾符,編譯器會預設將他們認定為__autoreleasing型別。
比如下面的兩段程式碼是等同的:
1 2 3 4 |
- (NSString *)doSomething:(NSNumber **)value { // do something } |
1 2 3 4 |
- (NSString *)doSomething:(NSNumber * __autoreleasing *)value { // do something } |
除非你顯式得給value宣告瞭__strong,否則value預設就是__autoreleasing的。
最後一點,某些類的方法會隱式地使用自己的autorelease pool,在這種時候使用__autoreleasing型別要特別小心。
比如NSDictionary的[enumerateKeysAndObjectsUsingBlock]方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error { [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ // do stuff if (there is some error && error != nil) { *error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil]; }  }]; } |
會隱式地建立一個autorelease pool,上面程式碼實際類似於:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error { [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ @autoreleasepool // 被隱式建立 { if (there is some error && error != nil) { *error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil]; }  } }]; // *error 在這裡已經被dict的做列舉遍歷時建立的autorelease pool釋放掉了 :( } |
為了能夠正常的使用*error,我們需要一個strong型的臨時引用,在dict的列舉Block中是用這個臨時引用,保證引用指向的物件不會在出了dict的列舉Block後被釋放,正確的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error { __block NSError* tempError; // 加__block保證可以在Block內被修改 [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { if (there is some error) { *tempError = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil]; }  }] if (error != nil) { *error = tempError; }  } |
__unsafe_unretained
ARC是在iOS 5引入的,而這個修飾符主要是為了在ARC剛釋出時相容iOS 4以及版本更低的裝置,因為這些版本的裝置沒有weak pointer system,簡單的理解這個系統就是我們上面講weak時提到的,能夠在weak引用指向物件被釋放後,把引用值自動設為nil的系統。這個修飾符在定義property時對應的是”unsafe_unretained”,實際可以將它理解為MRC時代的assign:純粹只是將引用指向物件,沒有任何額外的操作,在指向物件被釋放時依然原原本本地指向原來被釋放的物件(所在的記憶體區域)。所以非常不安全。
現在可以完全忽略掉這個修飾符了,因為iOS 4早已退出歷史舞臺很多年。
*使用修飾符的正確姿勢(方式=。=)
這可能是很多人都不知道的一個問題,包括之前的我,但卻是一個特別要注意的問題。
蘋果的文件中明確地寫道:
You should decorate variables correctly. When using qualifiers in an object variable declaration,
the correct format is:
1 <span style="color: #888888;">ClassName * qualifier variableName;</span>
按照這個說明,要定義一個weak型的NSString引用,它的寫法應該是:
1 |
NSString * __weak str = @"hehe"; // 正確! |
而不應該是:
1 |
__weak NSString *str = @"hehe"; // 錯誤! |
我相信很多人都和我一樣,從開始用ARC就一直用上面那種錯誤的寫法。
那這裡就有疑問了,既然文件說是錯誤的,為啥編譯器不報錯呢?文件又解釋道:
Other variants are technically incorrect but are “forgiven” by the compiler. To understand the issue, seehttp://cdecl.org/.
好吧,看來是蘋果爸爸(=。=)考慮到很多人會用錯,所以在編譯器這邊貼心地幫我們忽略並處理掉了這個錯誤:)雖然不報錯,但是我們還是應該按照正確的方式去使用這些修飾符,如果你以前也常常用錯誤的寫法,那看到這裡記得以後不要這麼寫了,哪天編譯器怒了,再不支援錯誤的寫法,就要鬱悶了。
棧中指標預設值為nil
無論是被strong,weak還是autoreleasing修飾,宣告在棧中的指標預設值都會是nil。所有這型別的指標不用再初始化的時候置nil了。雖然好習慣是最重要的,但是這個特性更加降低了“野指標”出現的可能性。
在ARC中,以下程式碼會輸出null而不是crash:)
1 2 3 4 5 |
- (void)myMethod { NSString *name; NSLog(@"name: %@", name); } |
ARC與Block
在MRC時代,Block會隱式地對進入其作用域內的物件(或者說被Block捕獲的指標指向的物件)加retain,來確保Block使用到該物件時,能夠正確的訪問。
這件事情在下面程式碼展示的情況中要更加額外小心。
1 2 3 4 5 6 7 8 9 10 |
MyViewController *myController = [[MyViewController alloc] init…]; // 隱式地呼叫[myController retain];造成迴圈引用 myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; [self presentViewController:myController animated:YES completion:^{ [myController release]; // 注意,這裡呼叫[myController release];是在MRC中的一個常規寫法,並不能解決上面迴圈引用的問題 }]; |
在這段程式碼中,myController的completionHandler呼叫了myController的方法[dismissViewController…],這時completionHandler會對myController做retain操作。而我們知道,myController對completionHandler也至少有一個retain(一般準確講是copy),這時就出現了在記憶體管理中最糟糕的情況:迴圈引用!簡單點說就是:myController retain了completionHandler,而completionHandler也retain了myController。迴圈引用導致了myController和completionHandler最終都不能被釋放。我們在delegate關係中,對delegate指標用weak就是為了避免這種問題。
不過好在,編譯器會及時地給我們一個警告,提醒我們可能會發生這型別的問題:
對這種情況,我們一般用如下方法解決:給要進入Block的指標加一個__block修飾符。
這個__block在MRC時代有兩個作用:
- 說明變數可改
- 說明指標指向的物件不做這個隱式的retain操作
一個變數如果不加__block,是不能在Block裡面修改的,不過這裡有一個例外:static的變數和全域性變數不需要加__block就可以在Block中修改。
使用這種方法,我們對程式碼做出修改,解決了迴圈引用的問題:
1 2 3 4 5 6 |
MyViewController * __block myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; //之後正常的release或者retain |
在ARC引入後,沒有了retain和release等操作,情況也發生了改變:在任何情況下,__block修飾符的作用只有上面的第一條:說明變數可改。即使加上了__block修飾符,一個被block捕獲的強引用也依然是一個強引用。這樣在ARC下,如果我們還按照MRC下的寫法,completionHandler對myController有一個強引用,而myController對completionHandler有一個強引用,這依然是迴圈引用,沒有解決問題:(
於是我們還需要對原始碼做修改。簡單的情況我們可以這樣寫:
1 2 3 4 5 6 |
__block MyViewController * myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; myController = nil; // 注意這裡,保證了block結束myController強引用的解除 }; |
在completionHandler之後將myController指標置nil,保證了completionHandler對myController強引用的解除,不過也同時解除了myController對myController物件的強引用。這種方法過於簡單粗暴了,在大多數情況下,我們有更好的方法。
這個更好的方法就是使用weak。(或者為了考慮iOS4的相容性用unsafe_unretained,具體用法和weak相同,考慮到現在iOS4裝置可能已經絕跡了,這裡就不講這個方法了)(關於這個方法的本質我們後面會談到)
為了保證completionHandler這個Block對myController沒有強引用,我們可以定義一個臨時的弱引用weakMyViewController來指向原myController的物件,並把這個弱引用傳入到Block內,這樣就保證了Block對myController持有的是一個弱引用,而不是一個強引用。如此,我們繼續修改程式碼:
1 2 3 4 5 6 |
MyViewController *myController = [[MyViewController alloc] init…]; // ... MyViewController * __weak weakMyViewController = myController; myController.completionHandler = ^(NSInteger result) { [weakMyViewController dismissViewControllerAnimated:YES completion:nil]; }; |
這樣迴圈引用的問題就解決了,但是卻不幸地引入了一個新的問題:由於傳入completionHandler的是一個弱引用,那麼當myController指向的物件在completionHandler被呼叫前釋放,那麼completionHandler就不能正常的運作了。在一般的單執行緒環境中,這種問題出現的可能性不大,但是到了多執行緒環境,就很不好說了,所以我們需要繼續完善這個方法。
為了保證在Block內能夠訪問到正確的myController,我們在block內新定義一個強引用strongMyController來指向weakMyController指向的物件,這樣多了一個強引用,就能保證這個myController物件不會在completionHandler被呼叫前釋放掉了。於是,我們對程式碼再次做出修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
MyViewController *myController = [[MyViewController alloc] init…]; // ... MyViewController * __weak weakMyController = myController; myController.completionHandler = ^(NSInteger result) { MyViewController *strongMyController = weakMyController; if (strongMyController) { // ... [strongMyController dismissViewControllerAnimated:YES completion:nil]; // ... } else { // Probably nothing... } }; |
到此,一個完善的解決方案就完成了:)
官方文件對這個問題的說明到這裡就結束了,但是可能很多朋友會有疑問,不是說不希望Block對原myController物件增加強引用麼,這裡為啥堂而皇之地在Block內新定義了一個強引用,這個強引用不會造成迴圈引用麼?理解這個問題的關鍵在於理解被Block捕獲的引用和在Block內定義的引用的區別。為了搞得明白這個問題,這裡需要了解一些Block的實現原理,但由於篇幅的緣故,本文在這裡就不展開了,詳細的內容可以參考其他的文章,這裡特別推薦唐巧的文章和另外2位作者的博文:這個和這個,講的都比較清楚。
這裡假設大家已經對Block的實現原理有所瞭解了。我們就直入主題了!注意前方高能(=。=)
為了更清楚地說明問題,這裡用一個簡單的程式舉例。比如我們有如下程式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <stdio.h> int main() { int b = 10; int *a = &b; void (^blockFunc)() = ^(){ int *c = a; }; blockFunc(); return 1; } |
程式中,同為int型的指標,a是被Block捕獲的變數,而c是在Block內定義的變數。我們用clang -rewrite-objc處理後,可以看到如下程式碼:
原main函式:
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { int b = 10; int *a = &b; void (*blockFunc)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a); ((void (*)(__block_impl *))((__block_impl *)blockFunc)->FuncPtr)((__block_impl *)blockFunc); return 1; } |
Block的結構:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *a; // 被捕獲的引用 a 出現在了block的結構體裡面 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; |
實際執行的函式:
1 2 3 4 5 6 7 |
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *a = __cself->a; // bound by copy int *c = a; // 在block中宣告的引用 c 在函式中宣告,存在於函式棧上 } |
我們可以清楚得看到,a和c存在的位置完全不同,如果Block存在於堆上(在ARC下Block預設在堆上),那麼a作為Block結構體的一個成員,也自然會存在於堆上,而c無論如何,永遠位於Block內實際執行程式碼的函式棧內。這也導致了兩個變數生命週期的完全不同:c在Block的函式執行完畢,即會被釋放,而a呢,只有在Block被從堆上釋放的時候才會釋放。
回到我們的MyViewController的例子中,同上理,如果我們直接讓Block捕獲我們的myController引用,那麼這個引用會被複制後(引用型別也會被複制)作為Block的成員變數存在於其所在的堆空間中,也就是為Block增加了一個指向myController物件的強引用,這就是造成迴圈引用的本質原因。對於MyViewController的例子,Block的結構體可以理解是這個樣子:(準確的結構體肯定和以下這個有區別,但也肯定是如下這種形式:)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; MyViewController * __strong myController; // 被捕獲的強引用myController __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; |
而反觀我們給Block傳入一個弱引用weakMyController,這時我們Block的結構:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; MyViewController * __weak weakMyController; // 被捕獲的弱引用weakMyController __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; |
再看在Block內宣告的強引用strongMyController,它雖然是強引用,但存在於函式棧中,在函式執行期間,它一直存在,所以myController物件也一直存在,但是當函式執行完畢,strongMyController即被銷燬,於是它對myController物件的強引用也被解除,這時Block對myController物件就不存在強引用關係了!加入了strongMyController的函式大體會是這個樣子:
1 2 3 4 5 6 7 |
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { MyViewController * __strong strongMyController = __cself->weakMyController; // .... } |
綜上所述,在ARC下(在MRC下會略有不同),Block捕獲的引用和Block內宣告的引用無論是存在空間與生命週期都是截然不同的,也正是這種不同,造成了我們對他們使用方式的區別。
以上就解釋了之前提到的所有問題,希望大家能看明白:)
好的,最後再提一點,在ARC中,對Block捕獲物件的記憶體管理已經簡化了很多,由於沒有了retain和release等操作,實際只需要考慮迴圈引用的問題就行了。比如下面這種,是沒有記憶體洩露的問題的:
1 2 3 4 5 6 7 8 9 |
TestObject *aObject = [[TestObject alloc] init]; aObject.name = @"hehe"; self.aBlock = ^(){ NSLog(@"aObject's name = %@",aObject.name); }; |
我們上面提到的解決方案,只是針對Block產生迴圈引用的問題,而不是說所有的Block捕獲引用都要這麼處理,一定要注意!
ARC與Toll-Free Bridging
There are a number of data types in the Core Foundation framework and the Foundation framework that can be used interchangeably. This capability, called toll-free bridging, means that you can use the same data type as the parameter to a Core Foundation function call or as the receiver of an Objective-C message.
Toll-Free Briding保證了在程式中,可以方便和諧的使用Core Foundation型別的物件和Objective-C型別的物件。詳細的內容可參考官方文件。以下是官方文件中給出的一些例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"]; CFLocaleRef gbCFLocale = (CFLocaleRef) gbNSLocale; CFStringRef cfIdentifier = CFLocaleGetIdentifier (gbCFLocale); NSLog(@"cfIdentifier: %@", (NSString *)cfIdentifier); // logs: "cfIdentifier: en_GB" CFRelease((CFLocaleRef) gbNSLocale); CFLocaleRef myCFLocale = CFLocaleCopyCurrent(); NSLocale * myNSLocale = (NSLocale *) myCFLocale; [myNSLocale autorelease]; NSString *nsIdentifier = [myNSLocale localeIdentifier]; CFShow((CFStringRef) [@"nsIdentifier: " stringByAppendingString:nsIdentifier]); // logs identifier for current locale |
在MRC時代,由於Objective-C型別的物件和Core Foundation型別的物件都是相同的release和retain操作規則,所以Toll-Free Bridging的使用比較簡單,但是自從ARC加入後,Objective-C型別的物件記憶體管理規則改變了,而Core Foundation依然是之前的機制,換句話說,Core Foundation不支援ARC。
這個時候就必須要要考慮一個問題了,在做Core Foundation與Objective-C型別轉換的時候,用哪一種規則來管理物件的記憶體。顯然,對於同一個物件,我們不能夠同時用兩種規則來管理,所以這裡就必須要確定一件事情:哪些物件用Objective-C(也就是ARC)的規則,哪些物件用Core Foundation的規則(也就是MRC)的規則。或者說要確定物件型別轉換了之後,記憶體管理的ownership的改變。
If you cast between Objective-C and Core Foundation-style objects, you need to tell the compiler about the ownership semantics of the object using either a cast (defined in
objc/runtime.h
) or a Core Foundation-style macro (defined inNSObject.h
)
於是蘋果在引入ARC之後對Toll-Free Bridging的操作也加入了對應的方法與修飾符,用來指明用哪種規則管理記憶體,或者說是記憶體管理權的歸屬。
這些方法和修飾符分別是:
__bridge(修飾符)
只是宣告型別轉變,但是不做記憶體管理規則的轉變。
比如:
1 |
CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"Hello, %@!", name]; |
只是做了NSString到CFStringRef的轉化,但管理規則未變,依然要用Objective-C型別的ARC來管理s1,你不能用CFRelease()去釋放s1。
__bridge_retained(修飾符)
or CFBridgingRetain(函式)
表示將指標型別轉變的同時,將記憶體管理的責任由原來的Objective-C交給Core Foundation來處理,也就是,將ARC轉變為MRC。
比如,還是上面那個例子
1 2 3 4 5 |
NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name]; CFStringRef s2 = (__bridge_retained CFStringRef)s1; // do something with s2 //... CFRelease(s2); // 注意要在使用結束後加這個 |
我們在第二行做了轉化,這時記憶體管理規則由ARC變為了MRC,我們需要手動的來管理s2的記憶體,而對於s1,我們即使將其置為nil,也不能釋放記憶體。
等同的,我們的程式也可以寫成:
1 2 3 4 5 |
NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name]; CFStringRef s2 = (CFStringRef)CFBridgingRetain(s1); // do something with s2 //... CFRelease(s2); // 注意要在使用結束後加這個 |
__bridge_transfer(修飾符)
or CFBridgingRelease(函式)
這個修飾符和函式的功能和上面那個__bridge_retained相反,它表示將管理的責任由Core Foundation轉交給Objective-C,即將管理方式由MRC轉變為ARC。
比如:
1 2 3 4 |
CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .); NSString *s = (__bridge_transfer NSString *)result; //or NSString *s = (NSString *)CFBridgingRelease(result); return s; |
這裡我們將result的管理責任交給了ARC來處理,我們就不需要再顯式地將CFRelease()了。
對了,這裡你可能會注意到一個細節,和ARC中那個4個主要的修飾符(__strong,__weak,…)不同,這裡修飾符的位置是放在型別前面的,雖然官方文件中沒有說明,但看官方的標頭檔案可以知道。小夥伴們,記得別把位置寫錯哦:)
呼~ 好了,以上就是本篇文章的主要內容。這次採用了新的排版,感覺比以前有條理得多,希望的大家看的舒服。