本文來源於我我的的ARC學習筆記,旨在經過簡明扼要的方式總結出iOS開發中ARC(Automatic Reference Counting,自動引用計數)內存管理技術的要點,因此不會涉及所有細節。這篇文章不是一篇標準的ARC使用教程,並假定讀者已經對ARC有了必定了解和使用經驗。詳細的關於ARC的信息請參見蘋果的官方文檔與網上的其餘教程:)html
本文的主要內容:ios
ARC的本質git
ARC的開啓與關閉github
ARC的修飾符安全
ARC與Block多線程
ARC與Toll-Free Bridgingapp
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
不一樣於XCode4能夠在建立工程時選擇關閉ARC,XCode5在建立的工程是默認開啓ARC,沒有能夠關閉ARC的選項。
若是須要對特定文件開啓或關閉ARC,能夠在工程選項中選擇Targets -> Compile Phases -> Compile Sources,在裏面找到對應文件,添加flag:
打開ARC:-fobjc-arc
關閉ARC:-fno-objc-arc
如圖:
ARC主要提供了4種修飾符,他們分別是:__strong,__weak,__autoreleasing,__unsafe_unretained。
表示引用爲強引用。對應在定義property時的"strong"。全部對象只有當沒有任何一個強引用指向時,纔會被釋放。
注意:若是在聲明引用時不加修飾符,那麼引用將默認是強引用。當須要釋放強引用指向的對象時,須要將強引用置nil。
表示引用爲弱引用。對應在定義property時用的"weak"。弱引用不會影響對象的釋放,即只要對象沒有任何強引用指向,即便有100個弱引用對象指向也沒用,該對象依然會被釋放。不過好在,對象在被釋放的同時,指向它的弱引用會自動被置nil,這個技術叫zeroing weak pointer。這樣有效得防止無效指針、野指針的產生。__weak通常用在delegate關係中防止循環引用或者用來修飾指向由Interface Builder編輯與生成的UI控件。
表示在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;
} ?
}
|
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:
1ClassName * 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/.
好吧,看來是蘋果爸爸(=。=)考慮到不少人會用錯,因此在編譯器這邊貼心地幫咱們忽略並處理掉了這個錯誤:)雖然不報錯,可是咱們仍是應該按照正確的方式去使用這些修飾符,若是你之前也經常用錯誤的寫法,那看到這裏記得之後不要這麼寫了,哪天編譯器怒了,再不支持錯誤的寫法,就要鬱悶了。
不管是被strong,weak仍是autoreleasing修飾,聲明在棧中的指針默認值都會是nil。全部這類型的指針不用再初始化的時候置nil了。雖然好習慣是最重要的,可是這個特性更加下降了「野指針」出現的可能性。
在ARC中,如下代碼會輸出null而不是crash:)
1
2
3
4
5
|
- (void)myMethod
{
NSString *name;
NSLog(@
"name: %@"
, name);
}
|
在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捕獲引用都要這麼處理,必定要注意!
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的操做也加入了對應的方法與修飾符,用來指明用哪一種規則管理內存,或者說是內存管理權的歸屬。
這些方法和修飾符分別是:
只是聲明類型轉變,可是不作內存管理規則的轉變。
好比:
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,...)不一樣,這裏修飾符的位置是放在類型前面的,雖然官方文檔中沒有說明,但看官方的頭文件能夠知道。小夥伴們,記得別把位置寫錯哦:)
呼~ 好了,以上就是本篇文章的主要內容。