zoukankan      html  css  js  c++  java
  • IOS开发之 __bridge __bridge_transfer和__bridge_retained


    Core Foundation 框架


    Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。以下列举该框架支持进行管理的数据以及可提供的服务:

    群体数据类型 (数组、集合等)
    程序包
    字符串管理
    日期和时间管理
    原始数据块管理
    偏好管理
    URL及数据流操作
    线程和RunLoop
    port和soket通讯
    Core Foundation框架和Foundation框架紧密相关,它们为同样功能提供接口,但Foundation框架提供Objective-C接口。

    假设您将Foundation对象和Core Foundation类型掺杂使用,则可利用两个框架之间的 “toll-free bridging”。所谓的Toll-free bridging是说您能够在某个框架的方法或函数同一时候使用Core Foundatio和Foundation 框架中的某些类型。非常多数据类型支持这一特性,当中包含群体和字符串数据类型。每一个框架的类和类型描写叙述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。

    如需进一步信息,请阅读Core Foundation 框架參考。


    自 Xcode4.2 開始导入ARC机制后,为了支持对象间的转型,Apple又添加了很多转型用的keyword。这一讲我们就来了解其使用方法,以及产生的理由。

    引子
    我们先来看一下ARC无效的时候,我们写id类型转void*类型的写法:

    id obj = [[NSObject alloc] init];
    void *p = obj;
    反过来,当把void*对象变回id类型时,仅仅是简单地例如以下来写,

    id obj = p;
    [obj release];
    可是上面的代码在ARC有效时。就有了以下的错误:

        error: implicit conversion of an Objective-C pointer
            to ’void *’ is disallowed with ARC
            void *p = obj;
                      ^
     
        error: implicit conversion of a non-Objective-C pointer
            type ’void *’ to ’id’ is disallowed with ARC
            id o = p;
                    ^

    __bridge
    为了解决这一问题。我们使用 __bridge keyword来实现id类型与void*类型的相互转换。

    看以下的样例。

    id obj = [[NSObject alloc] init];
     
    void *p = (__bridge void *)obj;
     
    id o = (__bridge id)p;
    将Objective-C的对象类型用 __bridge 转换为 void* 类型和使用 __unsafe_unretained keyword修饰的变量是一样的。

    被代入对象的全部者须要明白对象生命周期的管理,不要出现异常訪问的问题。

    除过 __bridge 以外,还有两个 __bridge 相关的类型转换keyword:

    __bridge_transfer
    __bridge_retained
    接下来,我们将看看这两个keyword的差别。

     

    __bridge_retained
    先来看使用 __bridge_retained keyword的样例程序:

    id obj = [[NSObject alloc] init];
     
    void *p = (__bridge_retained void *)obj;
    从名字上我们应该能理解其意义:类型被转换时。其对象的全部权也将被变换后变量所持有。假设不是ARC代码,类似以下的实现:

    id obj = [[NSObject alloc] init];
     
    void *p = obj;
    [(id)p retain];
    能够用一个实际的样例验证,对象全部权是否被持有。

    void *p = 0;
     
    {
        id obj = [[NSObject alloc] init];
        p = (__bridge_retained void *)obj;
    }
     
    NSLog(@"class=%@", [(__bridge id)p class]);
    出了大括号的范围后,p 仍然指向一个有效的实体。

    说明他拥有该对象的全部权。该对象没有由于出其定义范围而被销毁。

    __bridge_transfer
    相反。当想把本来拥有对象全部权的变量,在类型转换后,让其释放原先全部权的时候,须要使用 __bridge_transfer keyword。文字有点绕口,我们还是来看一段代码吧。

    假设ARC无效的时候,我们可能须要写以下的代码。

    // p 变量原先持有对象的全部权
    id obj = (id)p;
    [obj retain];
    [(id)p release];
    那么ARC有效后。我们能够用以下的代码来替换:

    // p 变量原先持有对象的全部权
    id obj = (__bridge_transfer id)p;
    能够看出来,__bridge_retained 是编译器替我们做了 retain 操作,而 __bridge_transfer 是替我们做了 release1。

    Toll-Free bridged
    在iOS世界,主要有两种对象:Objective-C 对象和 Core Foundation 对象0。

    Core Foundation 对象主要是有C语言实现的 Core Foundation Framework 的对象,当中也有对象引用计数的概念,仅仅是不是 Cocoa Framework::Foundation Framework 的 retain/release,而是自身的 CFRetain/CFRelease 接口。

    这两种对象间能够互相转换和操作,不使用ARC的时候,单纯的用C原因的类型转换,不须要消耗CPU的资源。所以叫做 Toll-Free bridged。比方 NSArray和CFArrayRef, NSString和CFStringRef。他们尽管属于不同的 Framework,可是具有同样的对象结构,所以能够用标准C的类型转换。

    比方不使用ARC时。我们用以下的代码:

    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = (CFStringRef)string;
    相同,Core Foundation类型向Objective-C类型转换时,也是简单地用标准C的类型转换就可以。

    可是在ARC有效的情况下,将出现类似以下的编译错误:

        Cast of Objective-C pointer type ‘NSString *’ to C pointer type ‘CFStringRef’ (aka ‘const struct __CFString *’) requires a bridged cast
        Use __bridge to convert directly (no change in ownership)
        Use __bridge_retained to make an ARC object available as a +1 ‘CFStringRef’ (aka ‘const struct __CFString *’)
    错误中已经提示了我们须要如何做:用 __bridge 或者 __bridge_retained 来转型,其区别就是变更对象的全部权。

    正由于Objective-C是ARC管理的对象,而Core Foundation不是ARC管理的对象。所以才要特意这样转换,这与id类型向void*转换是一个概念。

    也就是说,当这两种类型(有ARC管理,没有ARC管理)在转换时,须要告诉编译器如何处理对象的全部权。

    上面的样例,使用 __bridge/__bridge_retained 后的代码例如以下:

    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = (__bridge CFStringRef)string;
    仅仅是单纯地运行了类型转换。没有进行全部权的转移,也就是说,当string对象被释放的时候,cfString也不能被使用了。

    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = (__bridge_retained CFStringRef)string;
    ...
    CFRelease(cfString); // 因为Core Foundation的对象不属于ARC的管理范畴,所以须要自己release
    使用 __bridge_retained 能够通过转换目标处(cfString)的 retain 处理,来使全部权转移。即使 string 变量被释放,cfString 还是能够使用详细的对象。

    仅仅是有一点,因为Core Foundation的对象不属于ARC的管理范畴。所以须要自己release。

    实际上,Core Foundation 内部。为了实现Core Foundation对象类型与Objective-C对象类型的相互转换,提供了以下的函数。

    CFTypeRef  CFBridgingRetain(id  X)  {
        return  (__bridge_retained  CFTypeRef)X;
    }
     
    id  CFBridgingRelease(CFTypeRef  X)  {
        return  (__bridge_transfer  id)X;
    }
    所以,能够用 CFBridgingRetain 替代 __bridge_retained keyword:

    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = CFBridgingRetain(string);
    ...
    CFRelease(cfString); // 因为Core Foundation不在ARC管理范围内,所以须要主动release。


    __bridge_transfer
    全部权被转移的同一时候,被转换变量将失去对象的全部权。当Core Foundation对象类型向Objective-C对象类型转换的时候。会经经常使用到 __bridge_transfer keyword。

    CFStringRef cfString = CFStringCreate...();
    NSString *string = (__bridge_transfer NSString *)cfString;
     
    // CFRelease(cfString); 由于已经用 __bridge_transfer 转移了对象的全部权,所以不须要调用 release
    相同。我们能够使用 CFBridgingRelease() 来取代 __bridge_transfer keyword。

    CFStringRef cfString = CFStringCreate...();
    NSString *string = CFBridgingRelease(cfString);


    总结
    由上面的学习我们了解到 ARC 中类型转换的使用方法。那么我们实际使用中依照如何的原则或者方法来区分使用呢。以下我总结了几点关键要素。

    明白被转换类型是否是 ARC 管理的对象
    Core Foundation 对象类型不在 ARC 管理范畴内
    Cocoa Framework::Foundation 对象类型(即一般使用到的Objectie-C对象类型)在 ARC 的管理范畴内
    假设不在 ARC 管理范畴内的对象,那么要清楚 release 的责任应该是谁
    各种对象的生命周期是如何的
    1. 声明 id obj 的时候。事实上是缺省的申明了一个 __strong 修饰的变量,所以编译器自己主动地增加了 retain 的处理,所以说 __bridge_transfer keyword仅仅为我们做了 release 处理。


  • 相关阅读:
    [Err] 1064
    maven项目警告: Using platform encoding (UTF-8 actually) to copy filtered resources
    maven中引入oracle驱动报错Missing artifact com.oracle:ojdbc14:jar:10.2.0.4.0
    springMVC 中url后缀使用html,不能返回json数据,否则会报406错误
    网站并发量的计算方法
    win7旗舰版显示不了文件扩展名
    Java获取mysql数据库元数据
    spring中 context:property-placeholder 导入多个独立的 .properties配置文件
    Could not autowire field: private java.lang.Integer com.taotao.sso.service.impl.UserServiceImpl.SSO_
    linux Nginx服务开机自启
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7157536.html
Copyright © 2011-2022 走看看