• Core Foundation 对象不属于ARC的管辖范围(转)


    一个成功的商人是虎,一个失败的商人是狼,你和他们中心哪个打交道??

    像那闪烁的微光,欲望把我人生的路子照亮;夜色愈浓,它愈放射出刺眼的光线。自 Xcode4.2 开端导入ARC机制后,为了支撑对象间的转型,Apple又增长了很多转型用的关键字。这一讲我们就来懂得其用法,以及产生的来由。

    引子

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

    1
    2
    id obj = [[NSObject alloc] init];
    void *p = obj;

    反过来,当把void*对象变回id类型时,只是简单地如下来写,

    1
    2
    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?关键字来实现id类型与void*类型的彼此转换。看下面的例子。

    1
    2
    3
    4
    5
    id obj = [[NSObject alloc] init];
    
    void *p = (__bridge void *)obj;
    
    id o = (__bridge id)p;

    将Objective-C的对象类型用 __bridge 转换为 void* 类型和应用 __unsafe_unretained 关键字润饰的变量是一样的。被代入对象的所有者须要明白对象生命周期的经管,不要呈现异常接见的题目。

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

    • __bridge_retained
    • __bridge_transfer

    接下来,我们将看看这两个关键字的差别。

    __bridge_retained

    先来看应用 __bridge_retained 关键字的例子法度:

    1
    2
    3
    id obj = [[NSObject alloc] init];
    
    void *p = (__bridge_retained void *)obj;

    从名字上我们应当能懂得其意义:类型被转换时,其对象的所有权也将被变换后变量所持有。若是不是ARC代码,类似下面的实现:

    1
    2
    3
    4
    id obj = [[NSObject alloc] init];
    
    void *p = obj;
    [(idp retain];

    可以用一个实际的例子验证,对象所有权是否被持有。

    1
    2
    3
    4
    5
    6
    7
    8
    void *p = 0;
    
    {
        id obj = [[NSObject alloc] init];
        p = (__bridge_retained void *)obj;
    }
    
    NSLog(@"class=%@", [(__bridge idp class]);

    出了大括号的局限后,p 仍然指向一个有效的实体。申明他拥有该对象的所有权,该对象没有因为出其定义局限而被烧毁。

    __bridge_transfer

    相反,当想把底本拥有对象所有权的变量,在类型转换后,让其开释本来所有权的时辰,须要应用 __bridge_transfer 关键字。文字有点绕口,我们还是来看一段代码吧。

    若是ARC无效的时辰,我们可能须要写下面的代码。

    1
    2
    3
    4
    // p 变量本来持有对象的所有权
    id obj = (id)p;
    [obj retain];
    [(idp release];

    那么ARC有效后,我们可以用下面的代码来调换:

    1
    2
    // 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时,我们用下面的代码:

    1
    2
    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 后的代码如下:

    __bridge
    1
    2
    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = (__bridge CFStringRef)string;

    只是纯真地履行了类型转换,没有进行所有权的转移,也就是说,当string对象被开释的时辰,cfString也不克不及被应用了。

    __bridge_retained
    1
    2
    3
    4
    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对象类型的彼此转换,供给了下面的函数。

    1
    2
    3
    4
    5
    6
    7
    CFTypeRef  CFBridgingRetainid  X)  {
        return  (__bridge_retained  CFTypeRef)X;
    }
    
    id  CFBridgingReleaseCFTypeRef  X)  {
        return  (__bridge_transfer  id)X;
    }

    所以,可以用 CFBridgingRetain 调换 __bridge_retained 关键字:

    1
    2
    3
    4
    NSString *string = [NSString stringWithFormat:...];
    CFStringRef cfString = CFBridgingRetain(string);
    ...
    CFRelease(cfString); // 因为Core Foundation不在ARC经管局限内,所以须要主动release。
    
    __bridge_transfer

    所有权被转移的同时,被转换变量将落空对象的所有权。当Core Foundation对象类型向Objective-C对象类型转换的时辰,会经常用到 __bridge_transfer 关键字。

    1
    2
    3
    4
    CFStringRef cfString = CFStringCreate...();
    NSString *string = (__bridge_transfer NSString *)cfString;
    
    // CFRelease(cfString); 因为已经用 __bridge_transfer 转移了对象的所有权,所以不须要调用 release
    

    同样,我们可以应用 CFBridgingRelease() 来庖代 __bridge_transfer 关键字。

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

    总结

    由上方的进修我们懂得到 ARC 中类型转换的用法,那么我们实际应用中遵守如何的原则或者办法来区分应用呢,下面我总结了几点关键要素。

    • 明白被转换类型是否是 ARC 经管的对象
      • Core Foundation 对象类型不在 ARC 经管局限内
      • Cocoa Framework::Foundation 对象类型(即一般应用到的Objectie-C对象类型)在 ARC 的经管局限内
    • 若是不在 ARC 经管局限内的对象,那么要清楚 release 的义务应当是谁
    • 各类对象的生命周期是如何的

     

     
  • 相关阅读:
    P4910 帕秋莉的手环
    P3216 [HNOI2011]数学作业
    洛谷 P2894 [USACO08FEB]酒店
    [网络流24题]魔术球问题
    [网络流24题]飞行员配对方案问题
    [网络流24题]最小路径覆盖问题
    洛谷 P1503鬼子进村
    BZOJ 3631: [JLOI2014]松鼠的新家
    洛谷 P2922 [USACO08DEC]秘密消息Secret Message
    洛谷 P1379 八数码难题
  • 原文地址:https://www.cnblogs.com/xiaonanxia/p/2811070.html
Copyright © 2020-2023  润新知