美文网首页
__bridge、__bridge_retained和 __br

__bridge、__bridge_retained和 __br

作者: naiyi | 来源:发表于2017-10-25 18:29 被阅读90次

    (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

    相关文章

      网友评论

          本文标题:__bridge、__bridge_retained和 __br

          本文链接:https://www.haomeiwen.com/subject/rnfcpxtx.html