亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

2019-11-14 19:31:33
字體:
來源:轉載
供稿:網友

本文旨在通過簡明扼要的方式總結出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 NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRC2 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 NSError *__autoreleasing error; 2 ? (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) 3 ?{ 4   NSLog(, error); 5 }

 

(在上面的writeToFile方法中error參數的類型為(NSError *__autoreleasing *))

注意,如果你的error定義為了strong型,那么,編譯器會幫你隱式地做如下事情,保證最終傳入函數的參數依然是個__autoreleasing類型的引用。

1 NSError *error; 2 NSError *__autoreleasing tempError = error; // 編譯器添加 3 if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) 4 ?{ 5   error = tempError; // 編譯器添加 6   NSLog(@"Error: %@", error); 7 }

 

所以為了提高效率,避免這種情況,我們一般在定義error的時候將其(老老實實地=。=)聲明為__autoreleasing類型的:

1 NSError *__autoreleasing error;

 

在這里,加上__autoreleasing之后,相當于在MRC中對返回值error做了如下事情:

*error = [[[NSError alloc] init] autorelease];

 

*error指向的對象在創建出來后,被放入到了autoreleasing pool中,等待使用結束后的自動釋放,函數外error的使用者并不需要關心*error指向對象的釋放。

另外一點,在ARC中,所有這種指針的指針 (NSError **)的函數參數如果不加修飾符,編譯器會默認將他們認定為__autoreleasing類型。

比如下面的兩段代碼是等同的:

1 - (NSString *)doSomething:(NSNumber **)value2 {3         // do something  4 }

 

1 - (NSString *)doSomething:(NSNumber * __autoreleasing *)value2 {3         // do something  4 }

 

除非你顯式得給value聲明了__strong,否則value默認就是__autoreleasing的。

最后一點,某些類的方法會隱式地使用自己的autorelease pool,在這種時候使用__autoreleasing類型要特別小心。

比如NSDictionary的[enumerateKeysAndObjectsUsingBlock]方法:

 1 - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error 2 { 3     [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ 4   5           // do stuff   6           if (there is some error && error != nil) 7           { 8                 *error = [NSError errorWithDomain:@"MyError" ?code:1 userInfo:nil]; 9           }10 ?11     }];12 ?}

 

會隱式地創建一個autorelease pool,上面代碼實際類似于:

 1 - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error 2 { 3     [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){ 4   5           @autoreleasepool  // 被隱式創建 6       { 7               if (there is some error && error != nil) 8               { 9                     *error = [NSError errorWithDomain:@"MyError" ?code:1 userInfo:nil];10               }11 ?          }12     }];13  14     // *error 在這里已經被dict的做枚舉遍歷時創建的autorelease pool釋放掉了 :(  15 ?}

 

為了能夠正常的使用*error,我們需要一個strong型的臨時引用,在dict的枚舉Block中是用這個臨時引用,保證引用指向的對象不會在出了dict的枚舉Block后被釋放,正確的方式如下:

 1 - (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error 2 { 3   __block NSError* tempError; // 加__block保證可以在Block內被修改   4   [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) 5   {  6     if (there is some error)  7     {  8       *tempError = [NSError errorWithDomain:@"MyError" ?code:1 userInfo:nil];  9     } ? 10  11   }] 12  13   if (error != nil) 14   { 15     *error = tempError; 16   } ?17 }

 

__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:

ClassName * qualifier variableName;

 

按照這個說明,要定義一個weak型的NSString引用,它的寫法應該是:

NSString * __weak str = @"hehe"; // 正確!

 

而不應該是:

__weak NSString *str = @"hehe";  // 錯誤!

 

我相信很多人都和我一樣,從開始用ARC就一直用上面那種錯誤的寫法。

那這里就有疑問了,既然文檔說是錯誤的,為啥編譯器不報錯呢?文檔又解釋道:

Other variants are technically incorrect but are “forgiven” by the compiler. To understand the issue, see http://cdecl.org/.

好吧,看來是蘋果爸爸(=。=)考慮到很多人會用錯,所以在編譯器這邊貼心地幫我們忽略并處理掉了這個錯誤:)雖然不報錯,但是我們還是應該按照正確的方式去使用這些修飾符,如果你以前也常常用錯誤的寫法,那看到這里記得以后不要這么寫了,哪天編譯器怒了,再不支持錯誤的寫法,就要郁悶了。

棧中指針默認值為nil

無論是被strong,weak還是autoreleasing修飾,聲明在棧中的指針默認值都會是nil。所有這類型的指針不用再初始化的時候置nil了。雖然好習慣是最重要的,但是這個特性更加降低了“野指針”出現的可能性。

在ARC中,以下代碼會輸出null而不是crash:)

1 - (void)myMethod 2 {3     NSString *name;4     NSLog(@"name: %@", name);5 }

 

ARC與Block


在MRC時代,Block會隱式地對進入其作用域內的對象(或者說被Block捕獲的指針指向的對象)加retain,來確保Block使用到該對象時,能夠正確的訪問。

這件事情在下面代碼展示的情況中要更加額外小心。

 1 MyViewController *myController = [[MyViewController alloc] init…]; 2   3 // 隱式地調用[myController retain];造成循環引用 4 myController.completionHandler =  ^(NSInteger result) { 5    [myController dismissViewControllerAnimated:YES completion:nil]; 6 }; 7   8 [self presentViewController:myController animated:YES completion:^{ 9    [myController release]; // 注意,這里調用[myController release];是在MRC中的一個常規寫法,并不能解決上面循環引用的問題10 }];

 

在這段代碼中,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 MyViewController * __block myController = [[MyViewController alloc] init…];2 // ...3 myController.completionHandler =  ^(NSInteger result) {4     [myController dismissViewControllerAnimated:YES completion:nil];5 };6 //之后正常的release或者retain

 

在ARC引入后,沒有了retain和release等操作,情況也發生了改變:在任何情況下,__block修飾符的作用只有上面的第一條:說明變量可改。即使加上了__block修飾符,一個被block捕獲的強引用也依然是一個強引用。這樣在ARC下,如果我們還按照MRC下的寫法,completionHandler對myController有一個強引用,而myController對completionHandler有一個強引用,這依然是循環引用,沒有解決問題:(

于是我們還需要對原代碼做修改。簡單的情況我們可以這樣寫:

1 __block MyViewController * myController = [[MyViewController alloc] init…];2 // ...3 myController.completionHandler =  ^(NSInteger result) {4     [myController dismissViewControllerAnimated:YES completion:nil];5     myController = nil;  // 注意這里,保證了block結束myController強引用的解除6 };

 

在completionHandler之后將myController指針置nil,保證了completionHandler對myController強引用的解除,不過也同時解除了myController對myController對象的強引用。這種方法過于簡單粗暴了,在大多數情況下,我們有更好的方法。

這個更好的方法就是使用weak。(或者為了考慮iOS4的兼容性用unsafe_unretained,具體用法和weak相同,考慮到現在iOS4設備可能已經絕跡了,這里就不講這個方法了)(關于這個方法的本質我們后面會談到)

為了保證completionHandler這個Block對myController沒有強引用,我們可以定義一個臨時的弱引用weakMyViewController來指向原myController的對象,并把這個弱引用傳入到Block內,這樣就保證了Block對myController持有的是一個弱引用,而不是一個強引用。如此,我們繼續修改代碼:

1 MyViewController *myController = [[MyViewController alloc] init…];2 // ...3 MyViewController * __weak weakMyViewController = myController;4 myController.completionHandler =  ^(NSInteger result) {5     [weakMyViewController dismissViewControllerAnimated:YES completion:nil];6 };

 

這樣循環引用的問題就解決了,但是卻不幸地引入了一個新的問題:由于傳入completionHandler的是一個弱引用,那么當myController指向的對象在completionHandler被調用前釋放,那么completionHandler就不能正常的運作了。在一般的單線程環境中,這種問題出現的可能性不大,但是到了多線程環境,就很不好說了,所以我們需要繼續完善這個方法。

為了保證在Block內能夠訪問到正確的myController,我們在block內新定義一個強引用strongMyController來指向weakMyController指向的對象,這樣多了一個強引用,就能保證這個myController對象不會在completionHandler被調用前釋放掉了。于是,我們對代碼再次做出修改:

 1 MyViewController *myController = [[MyViewController alloc] init…]; 2 // ... 3 MyViewController * __weak weakMyController = myController; 4 myController.completionHandler =  ^(NSInteger result) { 5     MyViewController *strongMyController = weakMyController; 6   7   if (strongMyController) { 8         // ... 9         [strongMyController dismissViewControllerAnimated:YES completion:nil];10         // ...11     }12     else {13         // Probably nothing...14     }15 };

 

到此,一個完善的解決方案就完成了:)

官方文檔對這個問題的說明到這里就結束了,但是可能很多朋友會有疑問,不是說不希望Block對原myController對象增加強引用么,這里為啥堂而皇之地在Block內新定義了一個強引用,這個強引用不會造成循環引用么?理解這個問題的關鍵在于理解被Block捕獲的引用和在Block內定義的引用的區別。為了搞得明白這個問題,這里需要了解一些Block的實現原理,但由于篇幅的緣故,本文在這里就不展開了,詳細的內容可以參考其他的文章,這里特別推薦唐巧的文章和另外2位作者的博文:這個這個,講的都比較清楚。

這里假設大家已經對Block的實現原理有所了解了。我們就直入主題了!注意前方高能(=。=)

為了更清楚地說明問題,這里用一個簡單的程序舉例。比如我們有如下程序:

 1 #include < stdio.h> 2   3 int main() 4 { 5     int b = 10; 6       7     int *a = &b; 8       9     void (^blockFunc)() = ^(){10      11         int *c = a;12  13     };14      15     blockFunc();16      17     return 1;18 }

 

程序中,同為int型的指針,a是被Block捕獲的變量,而c是在Block內定義的變量。我們用clang -rewrite-objc處理后,可以看到如下代碼:

原main函數:

 1 int main() 2 { 3     int b = 10; 4   5     int *a = &b; 6   7     void (*blockFunc)() = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a); 8   9     ((void (*)(__block_impl *))((__block_impl *)blockFunc)->FuncPtr)((__block_impl *)blockFunc);10  11     return 1;12 }

 

Block的結構:

 1 struct __main_block_impl_0 { 2   struct __block_impl impl; 3   struct __main_block_desc_0* Desc; 4     5   int *a; // 被捕獲的引用 a 出現在了block的結構體里面 6     7   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { 8     impl.isa = &_NSConcreteStackBlock; 9     impl.Flags = flags;10     impl.FuncPtr = fp;11     Desc = desc;12   }13 };

 

實際執行的函數:

1 static void __main_block_func_0(struct __main_block_impl_0 *__cself) {2   int *a = __cself->a; // bound by copy3  4  5         int *c = a; // 在block中聲明的引用 c 在函數中聲明,存在于函數棧上6  7     }

 

我們可以清楚得看到,a和c存在的位置完全不同,如果Block存在于堆上(在ARC下Block默認在堆上),那么a作為Block結構體的一個成員,也自然會存在于堆上,而c無論如何,永遠位于Block內實際執行代碼的函數棧內。這也導致了兩個變量生命周期的完全不同:c在Block的函數運行完畢,即會被釋放,而a呢,只有在Block被從堆上釋放的時候才會釋放。

回到我們的MyViewController的例子中,同上理,如果我們直接讓Block捕獲我們的myController引用,那么這個引用會被復制后(引用類型也會被復制)作為Block的成員變量存在于其所在的堆空間中,也就是為Block增加了一個指向myController對象的強引用,這就是造成循環引用的本質原因。對于MyViewController的例子,Block的結構體可以理解是這個樣子:(準確的結構體肯定和以下這個有區別,但也肯定是如下這種形式:)

 1 struct __main_block_impl_0 { 2   struct __block_impl impl; 3   struct __main_block_desc_0* Desc; 4     5   MyViewController * __strong myController;  // 被捕獲的強引用myController 6     7   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { 8     impl.isa = &_NSConcreteStackBlock; 9     impl.Flags = flags;10     impl.FuncPtr = fp;11     Desc = desc;12   }13 };

 

而反觀我們給Block傳入一個弱引用weakMyController,這時我們Block的結構:

 1 struct __main_block_impl_0 { 2   struct __block_impl impl; 3   struct __main_block_desc_0* Desc; 4     5   MyViewController * __weak weakMyController;  // 被捕獲的弱引用weakMyController 6     7   __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_a, int flags=0) : a(_a) { 8     impl.isa = &_NSConcreteStackBlock; 9     impl.Flags = flags;10     impl.FuncPtr = fp;11     Desc = desc;12   }13 };

 

再看在Block內聲明的強引用strongMyController,它雖然是強引用,但存在于函數棧中,在函數執行期間,它一直存在,所以myController對象也一直存在,但是當函數執行完畢,strongMyController即被銷毀,于是它對myController對象的強引用也被解除,這時Block對myController對象就不存在強引用關系了!加入了strongMyController的函數大體會是這個樣子:

1 static void __main_block_func_0(struct __main_block_impl_0 *__cself) {2  3   MyViewController * __strong strongMyController = __cself->weakMyController; 4  5     // ....6  7     }

 

綜上所述,在ARC下(在MRC下會略有不同),Block捕獲的引用和Block內聲明的引用無論是存在空間與生命周期都是截然不同的,也正是這種不同,造成了我們對他們使用方式的區別。

以上就解釋了之前提到的所有問題,希望大家能看明白:)

好的,最后再提一點,在ARC中,對Block捕獲對象的內存管理已經簡化了很多,由于沒有了retain和release等操作,實際只需要考慮循環引用的問題就行了。比如下面這種,是沒有內存泄露的問題的:

1 TestObject *aObject = [[TestObject alloc] init];2      3 aObject.name = @"hehe";4  5 self.aBlock = ^(){6      7     NSLog(@"aObject's name = %@",aObject.name);8          9 };

 

我們上面提到的解決方案,只是針對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 NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"]; 2 CFLocaleRef gbCFLocale = (CFLocaleRef) gbNSLocale; 3 CFStringRef cfIdentifier = CFLocaleGetIdentifier (gbCFLocale); 4 NSLog(@"cfIdentifier: %@", (NSString *)cfIdentifier); 5 // logs: "cfIdentifier: en_GB" 6 CFRelease((CFLocaleRef) gbNSLocale); 7    8 CFLocaleRef myCFLocale = CFLocaleCopyCurrent(); 9 NSLocale * myNSLocale = (NSLocale *) myCFLocale;10 [myNSLocale autorelease];11 NSString *nsIdentifier = [myNSLocale localeIdentifier];12 CFShow((CFStringRef) [@"nsIdentifier: " stringByAppendingString:nsIdentifier]);13 // 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)的規則?;蛘哒f要確定對象類型轉換了之后,內存管理的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(修飾符)

只是聲明類型轉變,但是不做內存管理規則的轉變。

比如:

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 NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];2 ?CFStringRef s2 = (__bridge_retained CFStringRef)s1;3 ?// do something with s24 //...5 ?CFRelease(s2); // 注意要在使用結束后加這個

 

我們在第二行做了轉化,這時內存管理規則由ARC變為了MRC,我們需要手動的來管理s2的內存,而對于s1,我們即使將其置為nil,也不能釋放內存。

等同的,我們的程序也可以寫成:

1 NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];2 ?CFStringRef s2 = (CFStringRef)CFBridgingRetain(s1);3 ?// do something with s24 //...5 ?CFRelease(s2); // 注意要在使用結束后加這個

 

__bridge_transfer(修飾符) or CFBridgingRelease(函數)

這個修飾符和函數的功能和上面那個__bridge_retained相反,它表示將管理的責任由Core Foundation轉交給Objective-C,即將管理方式由MRC轉變為ARC。

比如:

1 CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .);2 ?NSString *s = (__bridge_transfer NSString *)result;3 //or NSString *s = (NSString *)CFBridgingRelease(result);4 ?return s;

 

這里我們將result的管理責任交給了ARC來處理,我們就不需要再顯式地將CFRelease()了。

對了,這里你可能會注意到一個細節,和ARC中那個4個主要的修飾符(__strong,__weak,...)不同,這里修飾符的位置是放在類型前面的,雖然官方文檔中沒有說明,但看官方的頭文件可以知道。小伙伴們,記得別把位置寫錯哦:)

以上就是本文全部內容...希望對大家有所幫助!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美在线观看网址综合| 尤物tv国产一区| 中文字幕一区二区精品| 日韩日本欧美亚洲| 国产亚洲欧美日韩美女| 亚洲va欧美va国产综合久久| 日韩在线播放视频| 国产成人在线一区| 亚洲国产高清高潮精品美女| 高清欧美性猛交| 国产专区欧美专区| 日韩av在线一区二区| 91在线观看免费观看| 国产亚洲欧美aaaa| 97国产suv精品一区二区62| 精品亚洲va在线va天堂资源站| 欧美性视频在线| 亚洲精品资源在线| 久久影视电视剧免费网站清宫辞电视| 欧美午夜www高清视频| 日韩在线观看免费网站| 日韩欧美中文免费| 正在播放国产一区| 亚洲国产91色在线| 亚洲高清免费观看高清完整版| 精品久久在线播放| 国产日韩在线免费| 欧美视频国产精品| 亚洲精品国产美女| 亚洲人av在线影院| 欧美成人国产va精品日本一级| 欧美另类老女人| 91精品视频在线播放| 日韩中文字幕网| 久久天天躁狠狠躁夜夜躁| 精品久久久视频| 久久99精品久久久久久青青91| 国产精品久久久久秋霞鲁丝| 色偷偷av亚洲男人的天堂| 成人羞羞国产免费| 国产精品96久久久久久又黄又硬| 亚洲自拍偷拍福利| 久久伊人精品天天| 中文字幕精品网| 亚洲护士老师的毛茸茸最新章节| 亚洲欧美日韩国产精品| 欧美性jizz18性欧美| 91久热免费在线视频| 日韩在线观看免费高清完整版| 国产精品久久久久久久久久东京| 国产性猛交xxxx免费看久久| 日韩午夜在线视频| 中文字幕最新精品| 高清视频欧美一级| 亚洲欧美自拍一区| 成人欧美一区二区三区在线| 亚洲视频在线免费观看| 欧美激情第一页xxx| 欧美另类xxx| 中文字幕日韩av电影| 久久久久久国产三级电影| 国产精品日韩专区| 国产精品扒开腿做| 成人黄色免费在线观看| 日韩在线国产精品| 国产在线观看一区二区三区| 亚洲va欧美va国产综合剧情| 成人xvideos免费视频| 久久久久久国产精品三级玉女聊斋| 日韩精品丝袜在线| 国产精品高潮呻吟久久av无限| 久久精品91久久久久久再现| 欧美性猛交xxxx免费看| 亚洲午夜小视频| 麻豆国产精品va在线观看不卡| 欧美黑人性生活视频| 97精品久久久中文字幕免费| 欧美性猛交xxxxx水多| 国产日韩欧美视频| 不卡在线观看电视剧完整版| 成人激情免费在线| 91精品久久久久久久久不口人| 91久久久久久久久| 高清一区二区三区日本久| 亚洲国产精品一区二区三区| 国产高清视频一区三区| 欧美有码在线观看视频| 国产偷亚洲偷欧美偷精品| 国产成人精品电影| 永久免费看mv网站入口亚洲| 中文字幕久热精品在线视频| 97在线免费观看视频| 国产日韩精品在线播放| 神马国产精品影院av| 欧美日韩国产精品一区二区三区四区| 日产精品99久久久久久| 少妇高潮久久77777| 日韩精品在线免费观看视频| 日本欧美精品在线| 欧美成人四级hd版| 久久人人爽人人爽爽久久| 日韩成人av在线播放| 亚洲精品小视频| 欧美成年人视频网站欧美| 精品露脸国产偷人在视频| 久久免费精品视频| 亚洲人成在线电影| 国产日韩中文在线| 在线播放国产一区二区三区| 欧美俄罗斯乱妇| 欧美专区中文字幕| 亚洲一级黄色片| 午夜免费日韩视频| 91亚洲午夜在线| 欧美成人免费小视频| 这里只有精品视频在线| 欧美乱大交xxxxx| 精品福利一区二区| 亚洲欧美一区二区三区情侣bbw| 欧美天天综合色影久久精品| 国产成人精品在线视频| 日韩欧美精品网站| 中文字幕亚洲第一| 久久久久国产精品免费| 亚洲娇小xxxx欧美娇小| 亚洲美女久久久| 亚洲国产日韩欧美综合久久| 97精品国产91久久久久久| 久99九色视频在线观看| 91亚洲va在线va天堂va国| 国产在线精品成人一区二区三区| 亚洲欧洲国产一区| 2023亚洲男人天堂| 国产精品高清免费在线观看| 97视频在线观看成人| 欧美日本啪啪无遮挡网站| 欧美亚洲成人网| 亚洲成人三级在线| 亚洲一二三在线| 精品高清美女精品国产区| 亚洲欧美另类人妖| 国产精品国产三级国产aⅴ浪潮| 中文字幕日韩电影| 久久五月天色综合| 国产视频精品自拍| 国产香蕉一区二区三区在线视频| 欧美在线一级视频| 少妇高潮久久77777| 亚洲全黄一级网站| 精品一区二区三区四区在线| 欧美激情精品久久久久久大尺度| 欧美性生交xxxxx久久久| 亚洲男人天堂网| 久久成人精品电影| 91亚洲精品久久久久久久久久久久| 久久69精品久久久久久久电影好| 黄色成人av网| 成人免费观看网址| 欧美电影免费观看| 欧美裸体男粗大视频在线观看| 国产性色av一区二区| 国产中文字幕亚洲| 亚洲成人av片在线观看| 亚洲免费视频在线观看|