(1)Foundation与Core Foundation对象
Foundation创建出来的对象就是Object-C对象, Core Foundation对象主要是使用C语言编写的Core Foundation 框架中,并使用引用计数的对象。在ARC 无效是,Core Foundation框架中的retain/release分别是CFRetain/CFRelease。Core Foundation 与 Foundation 框架创建出来的对象区别很小,可以在不同的框架中使用,Foundation框架的API生成并持有的对象可以用Core Foundation框架的API释放,反过来也可以。
因为Core Foundation对象与Foundation对象没有区别,转换不需要额外的CPU资源,因此也被称为“免桥接”Toll - Free - Bridge。
(2)显示转换id 和 void*
/* MRC环境下 转换*/
id obj = [[NSObject alloc] init];
void *p = obj;
id o = p;
[o release];
/*ARC环境下 转换*/
在ARC环境下上面代码会引起编译错误,(个人猜测MRC下Foundation与Core Foundation对象都需要手动管理,而ARC环境下Foundation对象内存不需要手动管理Core Foundation对象需要,所以不能直接转需要桥接)
id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;
但是__bridge并不转让对象所有者,其安全性和__unsafe_unretained修饰符相近,甚至更低,如果不注意对象持有者会引起悬垂指针。桥接转换还有另外两种方式,分别是__bridge_retained和__bridge_transfer转换
__bridge_retained转换可使要转换赋值的变量也持有所赋值的对象。在MRC下其源代码是:
id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];
ARC环境下可以写成:
void *p = 0;
{
id obj = [[NSObject alloc] init];
p = (__bridge_retained void *)obj;
}
NSLog(@“class = %@“, [(__bridge id) p]);
obj变量作用域虽然结束了,但是由于__bridge_retained转换使p处于持有该对象的状态,因此对象不会被释放,用完需要手动释放p。
__briege_transfer转换提供与__bridge_retained相反的动作,被转换的变量所持有的对象在改变量赋值后会随之释放。
id obj = (__bridge_transfer id)p;
/*在MRC下表述为*/
id obj = (id)p
[obj retain];
[(id)p release];
(3)Foundation与Core Foundation对象转换 (终于讲到重点了)
** Foundation ——> Core Foundation
/*MRC环境下*/
CFMutableArrayRef cfobject = NULL;
{
id obj = [[NSMutableArray alloc] init];
[obj retain]
cfObject = (CFMutableArrayRef)obj;
CFRetain(cfobject); //记得持有对象
[obj release];
}
CFShow(cfObject);
CFRelease(cfObject);
/*ARC环境下*/
CFMutableArrayRef cfobject = NULL;
{
id obj = [[NSMutableArray alloc] init];//ARC下obj内存管理修饰符默认为__string,所以obj将持有对象
cfObject = (__bridge_retained CFMutableArrayRef)obj; //cfobject已经持有对象
}
//超出作用域,obj释放,但是cfobject还持有对象,所以对象不会释放(ARC管理Foundation对象不管理Core Foundation对象内存)
CFShow(cfObject);
CFRelease(cfObject);
**Core Foundation ——> Foundation
/*MRC环境下*/
{
CFMutableArrayRef cfObject = CFArrayCreateMutable(KCFAllocatorDefault, 0, NULL);
id obj = (id)cfObject;
[obj retained];
CFRelease(cfobject);
NSLog(@“clase=%@“, obj);
[obj release];//此时对象不再拥有持有者,将被释放
}
/*ARC环境下*/
{
CFMutableArrayRef cfObject = CFArrayCreateMutable(KCFAllocatorDefault, 0, NULL);
id obj = (__bridge_transfer id)cfObject; //对象赋值完成后cfObject不在持有对象引用将被释放,但是obj默认修饰符为__strong,所以obj将持有对象
NSLog(@“clase=%@“, obj);
}
//超出作用域,obj释放,此时对象不再拥有持有者,将被释放。
个人总结下:__bridge_retained是在桥接后让Core Foundation对象变量持有对象,即让对象引用计数+1,__bridge_transfer桥接后让Core Foundation对象变量释放所持有的对象,即让对象引用计数-1。而__bridge除了桥接其他什么操作都不做。在ARC环境下,下面的操作是对等的:
cfObject = (__bridge CFMutableArrayRef)obj + CFRetain(cfObject) 与 cfObject = (__bridge_retained CFMutableArrayRef)obj
obj = (__bridge id)cfObject + CFRelease(cfObject) 与 obj = (__bridge_transfer id)cfObject
=======================================================
以上部分作者:mr_f_knight
链接:http://www.jianshu.com/p/df74a54b6b75
========================================================
原则上从oc转c使用__bridge,这样的话ARC释放oc对象的时候,c的CFRef也指向null。
从c转oc使用__bridge_transfer,将c对象的所有权转移到oc,当oc对象被ARC自动回收的时候,c的CFRef指向null。
另外,如果c转oc只使用__bridge,则oc对象不持有c对象的引用,被ARC回收的时候并不会让引用计数-1,则需要CFRelease手动释放c对象。
一般见不到__bridge_retain的用法。
例如AFNetworking中的用法:
static NSData * AFSecKeyGetData(SecKeyRef key) {
CFDataRef data = NULL;
__Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out);
return (__bridge_transfer NSData *)data;
_out:
if (data) {
CFRelease(data);
}
return nil;
}
如果SecItemExport发生错误,代码goto到_out: 那么就需要手动释放data指针引用的c对象。
而如果没有错误,那么把持有权转交给返回值的NSData的对象,则data的引用retain-1,接收返回值的指针变量retain+1,这样就可以通过ARC来控制释放了。
另外一个例子:
static inline NSString * AFContentTypeForPathExtension(NSString *extension) {
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
if (!contentType) {
return @"application/octet-stream";
} else {
return contentType;
}
}
这里没有涉及到向外传递c对象的引用,那么第一步和第二步完全可以不通过__bridge_transfer,而是用CFStringRef来从第一行和第二行传递变量。
写成这样
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
但这样就要通过CFRelease(UTI)释放,而使用__bridge_transfer就不用管UTI的引用计数了。
参考:http://www.jianshu.com/p/82cbacc7e07b