http://www.cnblogs.com/flyFreeZn/p/4264220.html
本文來源于我個人的ARC學(xué)習(xí)筆記,旨在通過簡明扼要的方式總結(jié)出iOS開發(fā)中ARC(Automatic Reference Counting,自動引用計數(shù))內(nèi)存管理技術(shù)的要點,所以不會涉及全部細節(jié)。這篇文章不是一篇標(biāo)準(zhǔn)的ARC使用教程,并假定讀者已經(jīng)對ARC有了一定了解和使用經(jīng)驗。詳細的關(guān)于ARC的信息請參見蘋果的官方文檔與網(wǎng)上的其他教程:)本文的主要內(nèi)容:ARC的本質(zhì)ARC的開啟與關(guān)閉ARC的修飾符ARC與BlockARC與Toll-Free BridgingARC的本質(zhì)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的管理方式)的一次改進,但它和之前的技術(shù)本質(zhì)上沒有區(qū)別。具體信息可以參考ARC編譯器官方文檔。ARC的開啟與關(guān)閉不同于XCode4可以在創(chuàng)建工程時選擇關(guān)閉ARC,XCode5在創(chuàng)建的工程是默認開啟ARC,沒有可以關(guān)閉ARC的選項。如果需要對特定文件開啟或關(guān)閉ARC,可以在工程選項中選擇Targets -> Compile Phases -> Compile Sources,在里面找到對應(yīng)文件,添加flag:打開ARC:-fobjc-arc關(guān)閉ARC:-fno-objc-arc如圖:ARC的修飾符ARC主要提供了4種修飾符,他們分別是:__strong,__weak,__autoreleasing,__unsafe_unretained。__strong表示引用為強引用。對應(yīng)在定義property時的"strong"。所有對象只有當(dāng)沒有任何一個強引用指向時,才會被釋放。注意:如果在聲明引用時不加修飾符,那么引用將默認是強引用。當(dāng)需要釋放強引用指向的對象時,需要將強引用置nil。__weak表示引用為弱引用。對應(yīng)在定義property時用的"weak"。弱引用不會影響對象的釋放,即只要對象沒有任何強引用指向,即使有100個弱引用對象指向也沒用,該對象依然會被釋放。不過好在,對象在被釋放的同時,指向它的弱引用會自動被置nil,這個技術(shù)叫zeroing weak pointer。這樣有效得防止無效指針、野指針的產(chǎn)生。__weak一般用在delegate關(guān)系中防止循環(huán)引用或者用來修飾指向由Interface Builder編輯與生成的UI控件。__autoreleasing表示在autorelease pool中自動釋放對象的引用,和MRC時代autorelease的用法相同。定義property時不能使用這個修飾符,任何一個對象的property都不應(yīng)該是autorelease型的。一個常見的誤解是,在ARC中沒有autorelease,因為這樣一個“自動釋放”看起來好像有點多余。這個誤解可能源自于將ARC的“自動”和autorelease“自動”的混淆。其實你只要看一下每個iOS App的main.m文件就能知道,autorelease不僅好好的存在著,并且變得更fashion了:不需要再手工被創(chuàng)建,也不需要再顯式得調(diào)用[drain]方法釋放內(nèi)存池。以下兩行代碼的意義是相同的。NSString *str = [[[NSString alloc] initWithFormat:@"hehe"] autorelease]; // MRCNSString *__autoreleasing str = [[NSString alloc] initWithFormat:@"hehe"]; // ARC 這里關(guān)于autoreleasepool就不做展開了,詳細地信息可以參考官方文檔或者其他文章。__autoreleasing在ARC中主要用在參數(shù)傳遞返回值(out-parameters)和引用傳遞參數(shù)(pass-by-reference)的情況下。__autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return.比如常用的NSError的使用:NSError *__autoreleasing error; if (![data writeToFile:filename options:NSDataWritingAtomic error:&error]) { NSLog(@"Error: %@", error); }(在上面的writeToFile方法中error參數(shù)的類型為(NSError *__autoreleasing *))注意,如果你的error定義為了strong型,那么,編譯器會幫你隱式地做如下事情,保證最終傳入函數(shù)的參數(shù)依然是個__autoreleasing類型的引用。復(fù)制代碼NSError *error; NSError *__autoreleasing tempError = error; // 編譯器添加 if (![data writeToFile:filename options:NSDataWritingAtomic error:&tempError]) { error = tempError; // 編譯器添加 NSLog(@"Error: %@", error); }復(fù)制代碼所以為了提高效率,避免這種情況,我們一般在定義error的時候?qū)⑵洌ɡ侠蠈崒嵉?。=)聲明為__autoreleasing類型的:NSError *__autoreleasing error;在這里,加上__autoreleasing之后,相當(dāng)于在MRC中對返回值error做了如下事情:*error = [[[NSError alloc] init] autorelease];*error指向的對象在創(chuàng)建出來后,被放入到了autoreleasing pool中,等待使用結(jié)束后的自動釋放,函數(shù)外error的使用者并不需要關(guān)心*error指向?qū)ο蟮尼尫?。另外一點,在ARC中,所有這種指針的指針 (NSError **)的函數(shù)參數(shù)如果不加修飾符,編譯器會默認將他們認定為__autoreleasing類型。比如下面的兩段代碼是等同的:- (NSString *)doSomething:(NSNumber **)value{? ? ? ? // do something? }- (NSString *)doSomething:(NSNumber * __autoreleasing *)value{? ? ? ? // do something? }除非你顯式得給value聲明了__strong,否則value默認就是__autoreleasing的。最后一點,某些類的方法會隱式地使用自己的autorelease pool,在這種時候使用__autoreleasing類型要特別小心。比如NSDictionary的[enumerateKeysAndObjectsUsingBlock]方法:復(fù)制代碼- (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];? ? ? ? ? }? ? }];}復(fù)制代碼會隱式地創(chuàng)建一個autorelease pool,上面代碼實際類似于:復(fù)制代碼- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error{? ? [dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop){? ? ? ? ? @autoreleasepool? // 被隱式創(chuàng)建 {? ? ? ? ? ? ? if (there is some error && error != nil)? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? *error = [NSError errorWithDomain:@"MyError" code:1 userInfo:nil];? ? ? ? ? ? ? }? ? ? ? ? }? ? }];? ? // *error 在這里已經(jīng)被dict的做枚舉遍歷時創(chuàng)建的autorelease pool釋放掉了 :(? }? ? 復(fù)制代碼為了能夠正常的使用*error,我們需要一個strong型的臨時引用,在dict的枚舉Block中是用這個臨時引用,保證引用指向的對象不會在出了dict的枚舉Block后被釋放,正確的方式如下:復(fù)制代碼- (void)loopThroughDictionary:(NSDictionary *)dict error:(NSError **)error{ __block NSError* tempError; // 加__block保證可以在Block內(nèi)被修改? [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; } } 復(fù)制代碼__unsafe_unretainedARC是在iOS 5引入的,而這個修飾符主要是為了在ARC剛發(fā)布時兼容iOS 4以及版本更低的設(shè)備,因為這些版本的設(shè)備沒有weak pointer system,簡單的理解這個系統(tǒng)就是我們上面講weak時提到的,能夠在weak引用指向?qū)ο蟊会尫藕?,把引用值自動設(shè)為nil的系統(tǒng)。這個修飾符在定義property時對應(yīng)的是"unsafe_unretained",實際可以將它理解為MRC時代的assign:純粹只是將引用指向?qū)ο?,沒有任何額外的操作,在指向?qū)ο蟊会尫艜r依然原原本本地指向原來被釋放的對象(所在的內(nèi)存區(qū)域)。所以非常不安全?,F(xiàn)在可以完全忽略掉這個修飾符了,因為iOS 4早已退出歷史舞臺很多年。*使用修飾符的正確姿勢(方式=。=)這可能是很多人都不知道的一個問題,包括之前的我,但卻是一個特別要注意的問題。蘋果的文檔中明確地寫道:You should decorate variables correctly. When using qualifiers in an object variable declaration,the correct format is:ClassName * qualifier variableName;按照這個說明,要定義一個weak型的NSString引用,它的寫法應(yīng)該是:NSString * __weak str = @"hehe"; // 正確!而不應(yīng)該是:__weak NSString *str = @"hehe";? // 錯誤!我相信很多人都和我一樣,從開始用ARC就一直用上面那種錯誤的寫法。那這里就有疑問了,既然文檔說是錯誤的,為啥編譯器不報錯呢?文檔又解釋道:Other variants are technically incorrect but are “forgiven” by the compiler. To understand the issue, seehttp://cdecl.org/.好吧,看來是蘋果爸爸(=。=)考慮到很多人會用錯,所以在編譯器這邊貼心地幫我們忽略并處理掉了這個錯誤:)雖然不報錯,但是我們還是應(yīng)該按照正確的方式去使用這些修飾符,如果你以前也常常用錯誤的寫法,那看到這里記得以后不要這么寫了,哪天編譯器怒了,再不支持錯誤的寫法,就要郁悶了。棧中指針默認值為nil無論是被strong,weak還是autoreleasing修飾,聲明在棧中的指針默認值都會是nil。所有這類型的指針不用再初始化的時候置nil了。雖然好習(xí)慣是最重要的,但是這個特性更加降低了“野指針”出現(xiàn)的可能性。在ARC中,以下代碼會輸出null而不是crash:)- (void)myMethod {? ? NSString *name;? ? NSLog(@"name: %@", name);}ARC與Block在MRC時代,Block會隱式地對進入其作用域內(nèi)的對象(或者說被Block捕獲的指針指向的對象)加retain,來確保Block使用到該對象時,能夠正確的訪問。這件事情在下面代碼展示的情況中要更加額外小心。復(fù)制代碼MyViewController *myController = [[MyViewController alloc] init…];// 隱式地調(diào)用[myController retain];造成循環(huán)引用myController.completionHandler =? ^(NSInteger result) {? [myController dismissViewControllerAnimated:YES completion:nil];};[self presentViewController:myController animated:YES completion:^{? [myController release]; // 注意,這里調(diào)用[myController release];是在MRC中的一個常規(guī)寫法,并不能解決上面循環(huán)引用的問題}];復(fù)制代碼在這段代碼中,myController的completionHandler調(diào)用了myController的方法[dismissViewController...],這時completionHandler會對myController做retain操作。而我們知道,myController對completionHandler也至少有一個retain(一般準(zhǔn)確講是copy),這時就出現(xiàn)了在內(nèi)存管理中最糟糕的情況:循環(huán)引用!簡單點說就是:myController retain了completionHandler,而completionHandler也retain了myController。循環(huán)引用導(dǎo)致了myController和completionHandler最終都不能被釋放。我們在delegate關(guān)系中,對delegate指針用weak就是為了避免這種問題。不過好在,編譯器會及時地給我們一個警告,提醒我們可能會發(fā)生這類型的問題:對這種情況,我們一般用如下方法解決:給要進入Block的指針加一個__block修飾符。這個__block在MRC時代有兩個作用:說明變量可改說明指針指向的對象不做這個隱式的retain操作一個變量如果不加__block,是不能在Block里面修改的,不過這里有一個例外:static的變量和全局變量不需要加__block就可以在Block中修改。使用這種方法,我們對代碼做出修改,解決了循環(huán)引用的問題:MyViewController * __block myController = [[MyViewController alloc] init…];// ...myController.completionHandler =? ^(NSInteger result) {? ? [myController dismissViewControllerAnimated:YES completion:nil];};//之后正常的release或者retain在ARC引入后,沒有了retain和release等操作,情況也發(fā)生了改變:在任何情況下,__block修飾符的作用只有上面的第一條:說明變量可改。即使加上了__block修飾符,一個被block捕獲的強引用也依然是一個強引用。這樣在ARC下,如果我們還按照MRC下的寫法,completionHandler對myController有一個強引用,而myController對completionHandler有一個強引用,這依然是循環(huán)引用,沒有解決問題:(于是我們還需要對原代碼做修改。簡單的情況我們可以這樣寫:__block MyViewController * myController = [[MyViewController alloc] init…];// ...myController.completionHandler =? ^(NSInteger result) {? ? [myController dismissViewControllerAnimated:YES completion:nil];? ? myController = nil;? // 注意這里,保證了block結(jié)束myController強引用的解除};在completionHandler之后將myController指針置nil,保證了completionHandler對myController強引用的解除,不過也同時解除了myController對myController對象的強引用。這種方法過于簡單粗暴了,在大多數(shù)情況下,我們有更好的方法。這個更好的方法就是使用weak。(或者為了考慮iOS4的兼容性用unsafe_unretained,具體用法和weak相同,考慮到現(xiàn)在iOS4設(shè)備可能已經(jīng)絕跡了,這里就不講這個方法了)(關(guān)于這個方法的本質(zhì)我們后面會談到)為了保證completionHandler這個Block對myController沒有強引用,我們可以定義一個臨時的弱引用weakMyViewController來指向原myController的對象,并把這個弱引用傳入到Block內(nèi),這樣就保證了Block對myController持有的是一個弱引用,而不是一個強引用。如此,我們繼續(xù)修改代碼:MyViewController *myController = [[MyViewController alloc] init…];// ...MyViewController * __weak weakMyViewController = myController;myController.completionHandler =? ^(NSInteger result) {? ? [weakMyViewController dismissViewControllerAnimated:YES completion:nil];};這樣循環(huán)引用的問題就解決了,但是卻不幸地引入了一個新的問題:由于傳入completionHandler的是一個弱引用,那么當(dāng)myController指向的對象在completionHandler被調(diào)用前釋放,那么completionHandler就不能正常的運作了。在一般的單線程環(huán)境中,這種問題出現(xiàn)的可能性不大,但是到了多線程環(huán)境,就很不好說了,所以我們需要繼續(xù)完善這個方法。為了保證在Block內(nèi)能夠訪問到正確的myController,我們在block內(nèi)新定義一個強引用strongMyController來指向weakMyController指向的對象,這樣多了一個強引用,就能保證這個myController對象不會在completionHandler被調(diào)用前釋放掉了。于是,我們對代碼再次做出修改:復(fù)制代碼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...? ? }};復(fù)制代碼到此,一個完善的解決方案就完成了:)官方文檔對這個問題的說明到這里就結(jié)束了,但是可能很多朋友會有疑問,不是說不希望Block對原myController對象增加強引用么,這里為啥堂而皇之地在Block內(nèi)新定義了一個強引用,這個強引用不會造成循環(huán)引用么?理解這個問題的關(guān)鍵在于理解被Block捕獲的引用和在Block內(nèi)定義的引用的區(qū)別。為了搞得明白這個問題,這里需要了解一些Block的實現(xiàn)原理,但由于篇幅的緣故,本文在這里就不展開了,詳細的內(nèi)容可以參考其他的文章,這里特別推薦唐巧的文章和另外2位作者的博文:這個和這個,講的都比較清楚。這里假設(shè)大家已經(jīng)對Block的實現(xiàn)原理有所了解了。我們就直入主題了!注意前方高能(=。=)為了更清楚地說明問題,這里用一個簡單的程序舉例。比如我們有如下程序:復(fù)制代碼#includeint main()
{
int b = 10;
int *a = &b;
void (^blockFunc)() = ^(){
int *c = a;
};
blockFunc();
return 1;
}
復(fù)制代碼
程序中,同為int型的指針,a是被Block捕獲的變量,而c是在Block內(nèi)定義的變量。我們用clang -rewrite-objc處理后,可以看到如下代碼:
原main函數(shù):
復(fù)制代碼
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;
}
復(fù)制代碼
Block的結(jié)構(gòu):
復(fù)制代碼
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *a; // 被捕獲的引用 a 出現(xiàn)在了block的結(jié)構(gòu)體里面
__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;
}
};
復(fù)制代碼
實際執(zhí)行的函數(shù):
復(fù)制代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *a = __cself->a; // bound by copy
int *c = a; // 在block中聲明的引用 c 在函數(shù)中聲明,存在于函數(shù)棧上
}
復(fù)制代碼
我們可以清楚得看到,a和c存在的位置完全不同,如果Block存在于堆上(在ARC下Block默認在堆上),那么a作為Block結(jié)構(gòu)體的一個成員,也自然會存在于堆上,而c無論如何,永遠位于Block內(nèi)實際執(zhí)行代碼的函數(shù)棧內(nèi)。這也導(dǎo)致了兩個變量生命周期的完全不同:c在Block的函數(shù)運行完畢,即會被釋放,而a呢,只有在Block被從堆上釋放的時候才會釋放。
回到我們的MyViewController的例子中,同上理,如果我們直接讓Block捕獲我們的myController引用,那么這個引用會被復(fù)制后(引用類型也會被復(fù)制)作為Block的成員變量存在于其所在的堆空間中,也就是為Block增加了一個指向myController對象的強引用,這就是造成循環(huán)引用的本質(zhì)原因。對于MyViewController的例子,Block的結(jié)構(gòu)體可以理解是這個樣子:(準(zhǔn)確的結(jié)構(gòu)體肯定和以下這個有區(qū)別,但也肯定是如下這種形式:)
復(fù)制代碼
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;
}
};
復(fù)制代碼
而反觀我們給Block傳入一個弱引用weakMyController,這時我們Block的結(jié)構(gòu):
復(fù)制代碼
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;
}
};
復(fù)制代碼
再看在Block內(nèi)聲明的強引用strongMyController,它雖然是強引用,但存在于函數(shù)棧中,在函數(shù)執(zhí)行期間,它一直存在,所以myController對象也一直存在,但是當(dāng)函數(shù)執(zhí)行完畢,strongMyController即被銷毀,于是它對myController對象的強引用也被解除,這時Block對myController對象就不存在強引用關(guān)系了!加入了strongMyController的函數(shù)大體會是這個樣子:
復(fù)制代碼
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
MyViewController * __strong strongMyController = __cself->weakMyController;
// ....
}
復(fù)制代碼
綜上所述,在ARC下(在MRC下會略有不同),Block捕獲的引用和Block內(nèi)聲明的引用無論是存在空間與生命周期都是截然不同的,也正是這種不同,造成了我們對他們使用方式的區(qū)別。
以上就解釋了之前提到的所有問題,希望大家能看明白:)
好的,最后再提一點,在ARC中,對Block捕獲對象的內(nèi)存管理已經(jīng)簡化了很多,由于沒有了retain和release等操作,實際只需要考慮循環(huán)引用的問題就行了。比如下面這種,是沒有內(nèi)存泄露的問題的:
復(fù)制代碼
TestObject *aObject = [[TestObject alloc] init];
aObject.name = @"hehe";
self.aBlock = ^(){
NSLog(@"aObject's name = %@",aObject.name);
};
復(fù)制代碼
我們上面提到的解決方案,只是針對Block產(chǎn)生循環(huán)引用的問題,而不是說所有的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類型的對象。詳細的內(nèi)容可參考官方文檔。以下是官方文檔中給出的一些例子:
復(fù)制代碼
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
復(fù)制代碼
在MRC時代,由于Objective-C類型的對象和Core Foundation類型的對象都是相同的release和retain操作規(guī)則,所以Toll-Free Bridging的使用比較簡單,但是自從ARC加入后,Objective-C類型的對象內(nèi)存管理規(guī)則改變了,而Core Foundation依然是之前的機制,換句話說,Core Foundation不支持ARC。
這個時候就必須要要考慮一個問題了,在做Core Foundation與Objective-C類型轉(zhuǎn)換的時候,用哪一種規(guī)則來管理對象的內(nèi)存。顯然,對于同一個對象,我們不能夠同時用兩種規(guī)則來管理,所以這里就必須要確定一件事情:哪些對象用Objective-C(也就是ARC)的規(guī)則,哪些對象用Core Foundation的規(guī)則(也就是MRC)的規(guī)則?;蛘哒f要確定對象類型轉(zhuǎn)換了之后,內(nèi)存管理的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的操作也加入了對應(yīng)的方法與修飾符,用來指明用哪種規(guī)則管理內(nèi)存,或者說是內(nèi)存管理權(quán)的歸屬。
這些方法和修飾符分別是:
__bridge(修飾符)
只是聲明類型轉(zhuǎn)變,但是不做內(nèi)存管理規(guī)則的轉(zhuǎn)變。
比如:
CFStringRef s1 = (__bridge CFStringRef) [[NSString alloc] initWithFormat:@"Hello, %@!", name];
只是做了NSString到CFStringRef的轉(zhuǎn)化,但管理規(guī)則未變,依然要用Objective-C類型的ARC來管理s1,你不能用CFRelease()去釋放s1。
__bridge_retained(修飾符) or CFBridgingRetain(函數(shù))
表示將指針類型轉(zhuǎn)變的同時,將內(nèi)存管理的責(zé)任由原來的Objective-C交給Core Foundation來處理,也就是,將ARC轉(zhuǎn)變?yōu)镸RC。
比如,還是上面那個例子
NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 = (__bridge_retained CFStringRef)s1;
// do something with s2
//...
CFRelease(s2); // 注意要在使用結(jié)束后加這個
我們在第二行做了轉(zhuǎn)化,這時內(nèi)存管理規(guī)則由ARC變?yōu)榱薓RC,我們需要手動的來管理s2的內(nèi)存,而對于s1,我們即使將其置為nil,也不能釋放內(nèi)存。
等同的,我們的程序也可以寫成:
NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
CFStringRef s2 = (CFStringRef)CFBridgingRetain(s1);
// do something with s2
//...
CFRelease(s2); // 注意要在使用結(jié)束后加這個
__bridge_transfer(修飾符) or CFBridgingRelease(函數(shù))
這個修飾符和函數(shù)的功能和上面那個__bridge_retained相反,它表示將管理的責(zé)任由Core Foundation轉(zhuǎn)交給Objective-C,即將管理方式由MRC轉(zhuǎn)變?yōu)锳RC。
比如:
CFStringRef result = CFURLCreateStringByAddingPercentEscapes(. . .);
NSString *s = (__bridge_transfer NSString *)result;
//or NSString *s = (NSString *)CFBridgingRelease(result);
return s;
這里我們將result的管理責(zé)任交給了ARC來處理,我們就不需要再顯式地將CFRelease()了。
對了,這里你可能會注意到一個細節(jié),和ARC中那個4個主要的修飾符(__strong,__weak,...)不同,這里修飾符的位置是放在類型前面的,雖然官方文檔中沒有說明,但看官方的頭文件可以知道。小伙伴們,記得別把位置寫錯哦:)
呼~ 好了,以上就是本篇文章的主要內(nèi)容。這次采用了新的排版,感覺比以前有條理得多,希望的大家看的舒服。
文章中如果有任何錯誤或者問題,可以在下面留言或者給我發(fā)信,期待你們的回復(fù)。
感謝大家的收看。
原創(chuàng)文章,轉(zhuǎn)載請注明出處,謝謝:)