iOS開發ARC內存管理技術要點

本文來源於我我的的ARC學習筆記,旨在經過簡明扼要的方式總結出iOS開發中ARC(Automatic Reference Counting,自動引用計數)內存管理技術的要點,因此不會涉及所有細節。這篇文章不是一篇標準的ARC使用教程,並假定讀者已經對ARC有了必定了解和使用經驗。詳細的關於ARC的信息請參見蘋果的官方文檔與網上的其餘教程:)html

本文的主要內容:ios

  • ARC的本質git

  • ARC的開啓與關閉github

  • ARC的修飾符安全

  • ARC與Block多線程

  • ARC與Toll-Free Bridgingapp

技術交流新QQ羣:414971585

ARC的本質


ARC是編譯器(時)特性,而不是運行時特性,更不是垃圾回收器(GC)。ide

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編譯器官方文檔oop

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; 
? (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) 
?{ 
  NSLog(, 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
ClassName * qualifier variableName;

按照這個說明,要定義一個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,...)不一樣,這裏修飾符的位置是放在類型前面的,雖然官方文檔中沒有說明,但看官方的頭文件能夠知道。小夥伴們,記得別把位置寫錯哦:)

呼~ 好了,以上就是本篇文章的主要內容。

轉自:http://www.cocoachina.com/ios/20150206/11121.html

相關文章
相關標籤/搜索