Core Foundation
Core Foundation 杠框架使用一個樸素的C語言編寫的純手工創(chuàng)建的對象系統(tǒng)。CF對象具有它們自己的音獨的引用計數(shù)系統(tǒng)。從其名稱中具有的單詞"Create"、"Copy"的函數(shù)獲得的對象在返回時將具有“+1的保留計數(shù)”。CFRetain(aCFObject)函數(shù)將把aCFObject的保留計數(shù)加1,而CFRelease(aCFObject)函數(shù)則會把aCFObject的保留計數(shù)減1。 當一個對象的保留計數(shù)歸零時,它將被取消分配。如果你從一個“Create”、“Copy”函數(shù)接收到對象,最終必須通過調(diào)用CFRelease來抵消創(chuàng)建或復制。如果不這樣做,就會產(chǎn)生內(nèi)存泄漏。
ARC不會使用這個單獨的引用計數(shù)系統(tǒng)自動化。 如果你在使用CF對象,就要負責理解手動引用計數(shù),并且正確地保留和釋放你的CF對象
ARC 與 Core Foundation 之間的免費橋接
從免費橋接的一方到別一方的強制轉(zhuǎn)換對于ARC而言都是一個問題。當把一個對象從CF強制轉(zhuǎn)成OC(或者采用相反方向)時,結(jié)果將得到單個對象,它的保留計數(shù)可以被兩個不同的不會彼此交流的系統(tǒng)操縱。ARC對CF的內(nèi)存管理約定一無所知,使事情更壞的是,你可以直接控制其中一個系統(tǒng)(CF),但不能控制另一個系統(tǒng)ARC。就像由兩位廚師一起做一碗湯一樣,會引起烹飪麻煩。
編譯器避免這種麻煩的方式是禁止在OC對與CF對象之間進行強制轉(zhuǎn)換。解決這個問題的方式是:通過一種稱為 橋接強制轉(zhuǎn)換(brige casts) 的特殊的強制轉(zhuǎn)換類型給ARC提供一些額外的信息 有以下三種情況
- 強制轉(zhuǎn)換不轉(zhuǎn)移所有權(quán)
- 強制轉(zhuǎn)換把所有權(quán)轉(zhuǎn)出CF并轉(zhuǎn)入ARC中
- 強制轉(zhuǎn)換把所有權(quán)轉(zhuǎn)出ARC 并轉(zhuǎn)入CF中
強制轉(zhuǎn)換不會轉(zhuǎn)移所有權(quán)
在這種情況下,一方只會從另一方臨時“借用”對象,而不會轉(zhuǎn)移所有權(quán)
__bridge告訴ARC在強制轉(zhuǎn)換下不會轉(zhuǎn)移所有權(quán)。
// CF---->OC
CFStringRef cfstr = CFStringCreateWithCString(NULL, "hello cfstring!", kCFStringEncodingUTF8);
NSString * ocstr = (__bridge NSString*)cfstr;
//只是類型的轉(zhuǎn)換,沒有進行所有權(quán)的交接
NSLog(@"%@",ocstr);
CFRelease(cfstr);
// 內(nèi)存管理仍需要所手動釋放
//OC --> CF
NSString * ocString = @"hello objective C string!";
CFStringRef cfstring = (__bridge CFStringRef)ocString;
// 只是類型轉(zhuǎn)換,對象釋放仍由ARC來進行管理,不需要進行手動釋放該cfstring
// 生成gif文件路徑
NSString * gifpath = [imgDir stringByAppendingPathComponent:@"newgif.gif"];
CFURLRef urlref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (__bridge CFStringRef)gifpath, kCFURLPOSIXPathStyle, NO);
CFRelease(urlref);
NSString * gifpath = [imgDir stringByAppendingPathComponent:@"newgif.gif"];
CFURLRef urlref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFBridgingRetain(gifpath)/**只做類型的轉(zhuǎn)換,不需要移交所有權(quán)*/, kCFURLPOSIXPathStyle, NO);//這里會造內(nèi)存泄漏
強制轉(zhuǎn)換把所有權(quán)轉(zhuǎn)出CF并轉(zhuǎn)入ARC中
__bridge_transfer強制轉(zhuǎn)換將把所有權(quán)從CF轉(zhuǎn)移給ARC。它告訴ARC在CF一方有一個沒有抵消的保留計數(shù),最終必須通過一個release抵消它。ARC負責發(fā)送該release消息
CFStringRef cfstring = CFStringCreateWithCString(NULL, "hello CFStringRef object", kCFStringEncodingUTF8);
NSString * ocString = (__bridge_transfer NSString*)cfstring;
// ARC 將接替CF的所有權(quán),負責ocString的釋放,其將會在不再使用ocString對象時,向該對象發(fā)送一條release消息
NSLog(@"%@",ocString);
強制轉(zhuǎn)換把所有權(quán)轉(zhuǎn)出ARC并轉(zhuǎn)入CF中
__bridge_retain強制轉(zhuǎn)換采用的是相反的方向,它把所有權(quán)從ARC中轉(zhuǎn)移給CF。它告訴ARC抵消對象創(chuàng)建的責任傳遞給了CF。當CF代碼用完對象后,它必須利用對象作為參數(shù)來調(diào)用CFRelease()
NSString * ocObject = @"我將放器ARC的內(nèi)存管理,轉(zhuǎn)入CF的內(nèi)存管理系統(tǒng)";
CFStringRef cfObject = (__bridge_retained CFStringRef)ocObject;
// CF接管現(xiàn)OC對象的所有權(quán),它的釋放由CF來負責,必須手動進行釋放
CFRelease(cfObject);
利用宏隱藏強制轉(zhuǎn)換
可以利用宏CFBrigingRelease()和CFBrigingRetain()分別隱藏__bridge_transfer和__bridge_retain強制轉(zhuǎn)換,來改進代碼的美感。
// 以下兩行代碼將做相同的事情
NSString * ocString = (__bridge_transfer NSString*)cfstring;
NSString * ocString = CFBridgingRelease(cfstring);
// 以下兩行代碼將做相同的事情
CFStringRef cfObject = (__bridge_retained CFStringRef)ocObject;
CFStringRef cfObject = CFBridgingRetain(ocObject);
與void*之間來回進行強制轉(zhuǎn)換
ARC禁止在對象指針與類型化void*的變量之間直接進行強制轉(zhuǎn)換。
其原因與ARC禁目對象指針與CF類型之間直接進行大多數(shù)強制轉(zhuǎn)換原因相同。如果把一個對象指針強制轉(zhuǎn)換成void*,將把該對象提供給一個未知量。ARC不會管理非OC指針,因為它無法跟蹤一個void*變量中存儲的對象所發(fā)生的事情,別外,如果把一個void*指針強轉(zhuǎn)為OC對象,ARC將無法確定對象的所有權(quán)狀態(tài)。
如果確信知道自己正在做什么,可以使用橋接來轉(zhuǎn)換支配ARC:
NSMutableArray * array = [NSMutableArray arrayWithObject:@"hello OC object"];
// void * cPointer = (void*)array;//Cast of Objective-C pointer type 'NSMutableArray *' to C pointer type 'void *' requires a bridged cast 這樣編譯器會不報錯
void * cPointer = (__bridge void*)array;//這樣是OK 的
小心: __bridge 強制轉(zhuǎn)沒有內(nèi)存管理的隱含意思,并且void*不是強引用,如果指向?qū)ο蟮乃械膹娨枚枷Я?,就會取消分配對象,并?code>void*變理可能保存一個指向死對象的指針.
ARC 和額外的自動釋放池
使用ARC,你將不知道特定的對象是否將被釋放或者自動釋放,或者它將被取消分配的確切時間,不過當你的代碼將創(chuàng)建許多只在短時間內(nèi)需要的對象時,仍然可以使用額外的自動釋放時來限制內(nèi)存占用:
for (int i = 0; i<10000; i++) {
@autoreleasepool{
// 創(chuàng)建監(jiān)時的對象
}
}
@autoreleasepool的作用域確保:在循環(huán)的單獨一次迭代中創(chuàng)建的任何臨時對象都會在不晚于循環(huán)的底部位置收到一個release消息,在那個位置@autoreleasepool的作用域?qū)⒔K結(jié),并會清空對應的自動釋放池,ARC可能決定在更早的時間給這樣的對象發(fā)送一條release消息,而不是自動釋放它們,但是額外的@autoreleasepool作用域?qū)柚闺S著循環(huán)的每次迭代而使用當前自動釋放池中的對象數(shù)量增加的情況發(fā)生。