淺談__bridge牲览,__bridge_retained墓陈,__bridge_transfer
通過OC對象與C語言轉(zhuǎn)換
和
通過OC對象與CF對象(Core Foundation)轉(zhuǎn)換來進行淺談。
基本概念
__bridge:只涉及對象類型轉(zhuǎn)換不涉及對象所有權(quán)的轉(zhuǎn)換第献;
__bridge_retained:OC對象轉(zhuǎn)換成CF對象贡必,將OC對象所有權(quán)交給CF對象,內(nèi)存需要自己管理庸毫;
__bridge_transfer:CF對象轉(zhuǎn)換成OC對象仔拟,將CF對象所有權(quán)交給OC對象,ARC可以自動管理內(nèi)存飒赃;
ARC環(huán)境下編譯器會自動管理OC對象的內(nèi)存利花,但是不會自動管理CF對象的內(nèi)存,當我們創(chuàng)建了一個CF對象以后就需要我們使用CFRelease將其手動釋放
以下通過代碼來說明:
id類型和void *類型的轉(zhuǎn)換
__bridge
在MRC下载佳,id類型和void *類型的轉(zhuǎn)換
id obj = [[NSObject alloc] init];
void *pointer = obj;
obj = pointer;
上面這段代碼放在ARC下炒事,會報錯。
id與void *如果只是單純的類型轉(zhuǎn)換蔫慧,那么只要添加__bridge即可挠乳,不會改變對象所有權(quán)。
另外CFBridgingRetain和CFBridgingRelease將在下面介紹。
ARC代碼如下:
id obj = [[NSObject alloc] init];
void *pointer = (__bridge void *)(obj);
obj = (__bridge id)(pointer);
如下代碼盟蚣,就會造成野指針
void *pointer = NULL;
{
id obj = [[NSObject alloc] init];
pointer = (__bridge void *)obj;
//代碼塊執(zhí)行完畢后,obj對象已經(jīng)自動釋放威蕉,此時指針變量pointer指向的是一塊被釋放的內(nèi)存空間
}
//打印引用計數(shù)的時候就會報野指針錯誤
NSLog(@"retain count:%ld", CFGetRetainCount((__bridge CFTypeRef)((__bridge id)pointer)));
__bridge_retained
ARC代碼如下:
void *pointer = NULL;
{
id obj = [[NSObject alloc] init];
pointer = (__bridge_retained void *)obj;
}
NSLog(@"retain count:%ld", CFGetRetainCount((__bridge CFTypeRef)((__bridge id)pointer))); //retain count:1
通過打印結(jié)果刁俭,我看可以看到指針變量pointer仍然指向一個有效的實體。說明它擁有該對象的所有權(quán)韧涨。
MRC代碼如下:
void *pointer = NULL;
{
id obj = [[NSObject alloc] init]; //retain count:1
pointer = [obj retain]; //retain count:2
[obj release]; //retain count:1
}
NSLog(@"retain count:%ld", [(id)pointer retainCount]); //retain count:1
結(jié)論: __bridge_retained,可使要轉(zhuǎn)換賦值的對象也持有所賦值的對象侮繁。
__bridge_transfer
ARC代碼如下:
void *pointer = NULL;
{
id obj = [[NSObject alloc] init];
pointer = (__bridge_retained void *)obj;
}
//pointer持有對象
id object = (__bridge_transfer id)pointer;
MRC代碼如下:
void *pointer = NULL;
{
id obj = [[NSObject alloc] init]; //retain count:1
pointer = [obj retain]; //retain count:2
[obj release]; //retain count:1
}
id object = (id)pointer; //retain count:1
[object retain]; //retain count:2
[(id)pointer release]; //retain count:1
結(jié)論: __bridge_transfer虑粥,被轉(zhuǎn)換賦值的對象賦值給變量后,隨即也自行釋放宪哩。
聲明 id object 的時候娩贷,其實是缺省的申明了一個 __strong 修飾的變量,所以編譯器自動地加入了 retain 的處理锁孟,所以說 __bridge_transfer 關(guān)鍵字只為我們做了 release 處理彬祖。
OC對象和CF對象(Core Foundation)的轉(zhuǎn)換
Core Foundation 對象主要是由C語言實現(xiàn)的,其中也有對象引用計數(shù)的概念品抽,只不過不是retain/release储笑,而是自身的 CFRetain/CFRelease。
其中有一個概念:Toll-Free Bridging
它表示的是在Core Foundation框架和Foundation框架之間交換使用數(shù)據(jù)類型的技術(shù)圆恤。
#if __has_feature(objc_arc)
// After using a CFBridgingRetain on an NSObject, the caller must take responsibility for calling CFRelease at an appropriate time.
NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) {
return (__bridge_retained CFTypeRef)X;
}
NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) {
return (__bridge_transfer id)X;
}
#else
// This function is intended for use while converting to ARC mode only.
NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) {
return X ? CFRetain((CFTypeRef)X) : NULL;
}
// This function is intended for use while converting to ARC mode only.
NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) {
return [(id)CFMakeCollectable(X) autorelease];
}
#endif
我們可以看到CFBridgingRetain和CFBridgingRelease突倍,其實就是__bridge_retained和__bridge_transfer。
并不是所有的CF對象都支持Toll-Free Bridging
我們可以參照蘋果文檔盆昙,查看可支持的類型表羽历。
下面是簡單的代碼示例:
__bridge
MRC代碼如下:
NSString *string = [NSString stringWithFormat:@"hui"];
CFStringRef stringRef = (CFStringRef)string;
上面代碼如果直接用在ARC,會報錯淡喜,可以自己嘗試秕磷。
然后
ARC代碼如下:
NSString *string = [NSString stringWithFormat:@"hui"];
CFStringRef stringRef = (__bridge CFStringRef)string;
CFBridgingRetain - OC對象轉(zhuǎn)CF對象
NSString *string = [NSString stringWithFormat:@"hui"];
CFStringRef stringRef = CFBridgingRetain(string);
// 由于CF對象不屬于ARC的管理,所以需要自己release
CFRelease(stringRef);
CFBridgingRelease - CF對象轉(zhuǎn)OC對象
CFStringRef stringRef = CFStringCreateWithCString(kCFAllocatorDefault, "hui", kCFStringEncodingUTF8);
NSString *string = CFBridgingRelease(stringRef);
//CFRelease(stringRef); 使用CFBridgingRelease轉(zhuǎn)移了對象所有權(quán)炼团,所以不用調(diào)用CFRelease
總結(jié)
- Core Foundation 對象類型不在 ARC 管理范疇澎嚣,所以需要自己手動釋放;
- __bridge只更改對象類型们镜,不會涉及對象所有權(quán)币叹,使用它時要注意野指針的情況;
- __bridge_retained(CFBridgingRetain)是將OC對象轉(zhuǎn)換成C語言變量或者CF對象模狭,類似于retain颈抚,可使要轉(zhuǎn)換賦值的對象也持有所賦值的對象;
- __bridge_transfer(CFBridgingRelease)是將C語言變量或者CF對象轉(zhuǎn)換成OC對象,類似于release贩汉,被轉(zhuǎn)換賦值的對象賦值給變量后驱富,隨即也自行釋放;
over!