总结于 IOS Tuturial 中 ARC两章,详细在dropbox pdf 文档。

Toll-Free Bridging

当你在 Objective-C 和 Core Foundation 对象之间进行转换时,就需要使用 Bridge cast。

今天的多数应用很少需要使用 Core Foundation,大多数工作都可以直接使 用 Objective-C 类来完成。但是某些底层 API,如 Core Graphics 和 Core Text, 都基于 Core Foundation,而且不太可能会有 Objective-C 的版本。幸运的是, iOS 的设计使得这两种类型的对象非常容易转换。

例如 NSString 和 CFStringRef 就可以同等对待,在任何地方都可以互换使 用,背后的设计就是toll-free bridging。在 ARC 之前,只需要使用一个简单 的强制类型转换即可:

当然,alloc 分配了 NSString 对象,你需要在使用完之后进行释放,注意是 释放转换后的CFStringRef 对象:
CFRelease(s1);

反过来,从 Core Foundation 到 Objective-C 的方向也类似:

现在我们使用了 ARC,情况变得不一样!以下代码在手动内存管理中是完全 合法的,但在 ARC 中却存在问题:

- (NSString *)escape:(NSString *)text
{
CFStringRef s1 = (CFStringRef) [[NSString alloc] initWithFormat:@"Hello, %@!", name]; CFStringRef s2 = CFStringCreateWithCString(kCFAllocatorDefault, bytes,
kCFStringEncodingMacRoman); NSString *s3 = (NSString *)s2; // release the object when you're done
[s3 release];
return [(NSString *)CFURLCreateStringByAddingPercentEscapes( NULL, (CFStringRef)text,
NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", // 这里不需要 bridging casts,因为这是一个常量,不需要释放! CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin g)) autorelease];
}

首先需要移除 autorelease 调用。然后编译器还会报两个类型转换错误:

 Cast of C pointer type 'CFStringRef' to Objective-C pointer type 'NSString *' requires a bridged cast

 Cast of Objective-C pointer type 'NSString *' to C pointer type 'CFStringRef' requires a bridged cast

错误分别来自以下两行代码:

编译器必须知道由谁来负责释放转换后的对象,如果你把一个 NSObject 当 作 Core Foundation对象来使用,则 ARC 将不再负责释放该对象。但你必须明确 地告诉 ARC 你的这个意图,编译器没办法自己做主。同样如果你创建一个 Core Foundation 对象并把它转换为 NSObject 对象,你也必须告诉 ARC 占据对象的所 有权,并在适当的时候释放该对象。这就是所谓的 bridging casts。

CFURLCreateStringByAddingPercentEscapes()函数的参数需要两个 CFStringRef 对象,其中常量NSString 可以直接转换,因为不需要进行对象释 放;但是 text 参数不一样,它是传递进来的一个NSString 对象。而函数参数 和局部变量一样,都是 strong 指针,这种对象在函数入口处会被retain,并且 对象会持续存在直到指针被销毁(这里也就是函数返回时)。

对于 text 参数,我们希望 ARC 保持这个变量的所有权,同时又希望临时将 它当作 CFStringRef 对象来使用。这种情况下可以使用__bridge 说明符,它告 诉 ARC 不要更改对象的所有权,按普通规则释放该对象即可。

多数情况下,Objective-C 对象和 Core Foundation 对象之间互相转换时, 我们都应该使用__bridge。但是有时候我们确实需要给予 ARC 某个对象的所有权, 或者解除 ARC 对某个对象的所有权。这种情况下我们就需要使用另外两种 bridging casts:

  • __bridge_transfer:给予 ARC 所有权

  • __bridge_retained:解除 ARC 所有权

    在上面代码中,"return (NSString *)CFURLCreateStringByAddingPercentEscapes",编译器弹出的修复提示有两个:

    两个解决办法:__bridge 和 __bridge_transfer,正确的选择应该 是 __bridge_transfer。

    因为 CFURLCreateStringByAddingPercentEscapes() 函数创建了一个新 的 CFStringRef 对象,当然我们要的是 NSString 对象,因此我们使用了强制转 换。实际上我们真正想要做的是:

    从 CFURLCreateStringByAddingPercentEscapes 函数的 create 可以看 出,函数会返回一个retain 过的对象。某个人需要负责在适当的时候释放该对 象,如果我们不把这个对象返回为NSString,则通常我们需要自己调用:

    {

    }

    不过 ARC 只能作用于 Objective-C 对象,不能释放 Core Foundation 对象。 因此这里你仍然需要调用 CFRelease()来释放该对象。

    这里我们的真实意图是:转换新创建的 CFStringRef 对象为 NSString 对象, 并且当我们不再需要使用这个 NSString 对象时,ARC 能够适当地释放它。

    因此我们使用 __bridge_transfer告诉ARC:"嘿!ARC,这个CFStringRef 对象现在是一个NSString 对象了,我希望你来销毁它,我这里就不调用 CFRelease()来释放它了"。

    如果我们使用 __bridge,就会导致内存泄漏。ARC并不知道自己应该在使 用完对象之后释放该对象,也没有人调用 CFRelease()。结果这个对象就会永远 保留在内存中。因此选择正确的bridge 说明符是至关重要的。

    为了代码更加可读和容易理解,iOS 还提供了一个辅助函数: CFBridgingRelease()。函数所做事情和 __bridge_transfer 强制转换完全一 样,但更加简洁和清晰。CFBridgingRelease() 函数定义为内联函数,因此不会 导致额外的开销。函数之所以命名为 CFBridgingRelease(),是因为一般你会在 需要使用 CFRelease()释放对象的地方,调用 CFBridgingRelease()来传递对象的所有权。

    因此最后我们的代码如下:

    - (NSString *)escape:(NSString *)text

    {

    }

    另一个常见的需要 CFBridgingRelease 的情况是 AddressBook framework:

    - (NSString *)firstName

    {

    }

    只要你调用命名为 Create, Copy, Retain 的 Core Foundation 函数,你都 需要使用CFBridgingRelease()安全地将值传递给ARC。

    __bridge_retained 则正好相反,假设你有一个 NSString 对象,并且要将 它传递给某个 Core Foundation API,该函数希望接收这个 string 对象的所有 权。这时候你就不希望 ARC 也去释放该对象,否则就会对同一对象释放两次,而 且必将导致应用崩溃!换句话说,使用__bridge_retained 将对象的所有权给 予 Core Foundation,而 ARC 不再负责释放该对象。

    如下面例子所示:

    return CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(
    
    NULL,
    
    (__bridge CFStringRef)text,
    
    NULL,
    
    CFSTR("!*'();:@&=+$,/?%#[]"),
    
    CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncodin
    
    g)));
    
    return CFBridgingRelease(ABRecordCopyCompositeName(...));
    
    NSString *s1 = [[NSString alloc] initWithFormat:@"Hello, %@!", name];
    CFStringRef s2 = (__bridge_retained CFStringRef)s1;
    // do something with s2
    // . . .
    CFRelease(s2);

一旦 (__bridge_retained CFStringRef) 转换完成,ARC 就不再负责释放该 对象。如果你在这里使用 __bridge,应用就很可能会崩溃。ARC 可能在 Core Foundation 正在使用该对象时,释放掉它。

同样__bridge_retained 也有一个辅助函数:CFBridgingRetain()。从名字 就可以看出,这个函数会让 Core Foundation 执行 retain,实际如下:

现在你应该明白了,上面例子的 CFRelease()是和 CFBridgingRetain()对应 的。你应该很少需要使用__bridge_retained 或 CFBridgingRetain()。

__bridge 转换不仅仅局限于 Core Foundation 对象,某些 API 使用 void * 指针作为参数,允许你传递任何东西的引用:Objective-C 对象、Core Foundation 对象、malloc()内存缓冲区等等。void *表示这是一个指针,但实际的数据类型 可以是任何东西!

要将 Objective-C 对象和 void *互相转换,你也需要使用__bridge 转换, 如下:

在 animation delegate 方法中,你再将对象强制转回来:

{

}

总结:

  • 使用 CFBridgingRelease(),从 Core Foundation 传递所有权给 Objective-C;

  • 使用 CFBridgingRetain(),从 Objective-C 传递所有权给 Core Foundation;

  • 使用__brideg,表示临时使用某种类型,不改变对象的所有权。

ARC中__bridge, __bridge__transfer, __bridge_retained 关系的更多相关文章

  1. ARC中block块作为属性的使用笔记

    ARC中block块作为属性的使用笔记 block较难理解,根据在内存中的分布情况就分为3种类型,根据使用的情形又分为很多很多种.虽然用起来容易,但使用不当会造成内存泄露,虽然都是这么说,但你真的研究 ...

  2. @autoreleasepool在MRC和ARC中的区别

    对于@autoreleasepool {} (1)在ARC中会销毁所有在里面创建的对象,即使你用外面的Strong指针指向他 (2)在MRC中如果有外部的强指针指向,不会销毁对象,retainCoun ...

  3. ARC中KVO开发注意

    1 在ARC 中 KVO开发 添加监听和去掉监听必需 一一匹配,不要有过的去掉监听否则会有可能导致对象无法释放. 例如,在一个viewcontroller中添加webview 并监听webview的c ...

  4. block使用小结、在arc中使用block、如何防止循环引用

    引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...

  5. TCP/IP协议原理与应用笔记11:TCP/IP中地址与层次关系

    1. 网络中常用的地址: 2. TCP/IP中地址与层次关系 :

  6. 持续集成:TestNG中case之间的关系

    持续集成:TestNG中case之间的关系   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq: ...

  7. Programming In Scala笔记-第十一章、Scala中的类继承关系

    本章主要从整体层面了解Scala中的类层级关系. 一.Scala的类层级 在Java中Object类是所有类的最终父类,其他所有类都直接或间接的继承了Object类.在Scala中所有类的最终父类为A ...

  8. ASP.NET Core中使用GraphQL - 第九章 在GraphQL中处理多对多关系

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...

  9. 通过SQL脚本来查询SQLServer 中主外键关系

    在SQLServer中主外键是什么,以及主外键如何创建,在这里就不说了,不懂的可以点击这里,这篇文章也是博客园的博友写的,我觉得总结的很好: 此篇文章主要介绍通过SQL脚本来查看Sqlserver中主 ...

随机推荐

  1. Array.prototype.slice.call()方法的理解

    1.基础1)slice() 方法可从已有的数组中返回选定的元素. start:必需.规定从何处开始选取.如果是负数,那么它规定从数组尾部开始算起的位置.也就是说,-1 指最后一个元素,-2 指倒数第二 ...

  2. 蓝桥杯-PREV45-图形排版

    这是2017年蓝桥杯C组C++的压轴题,拿到之后没什么想法.但是蓝桥杯有部分分.所以直接敲了个大暴力提交上去过了一半的数据.后来想到了DP,但是没能实现出来,感觉还是有问题的.后来看了解题视频发现是预 ...

  3. 如果你有一个域名,你也可以免费有一个diy@yourdomain.com的企业邮局

    如果你有一个域名,例如:www.bengou.net那么你可以拥有一个免费邮箱:lajiyoujian@bengou.net.那么什么是企业邮局呢?有啥优点 企业邮局是指以您的域名作为后缀的电子邮件地 ...

  4. cxf整合spring代码

    导入jar包cxf的jar包 创建实体类 package com.yhd.webservice.cxf.server.poto; public class Person { private Strin ...

  5. percent|Cane|confess|ballot|conceal

    You got 20 percent of the answers right - that means one in every five.你20%的答案是对的,也就是说每5个中有1个是对的. N- ...

  6. makefile中的变量赋值

    在makefile中赋值方式有:'='.':='.'?='和'+='. A = a $(B) B = b all: echo $(A) #运行结果:echo a b a b 这种赋值方式是没有先后顺序 ...

  7. ES6下的Function.bind方法

    在JavaScript的使用中,this的指向问题始终是一个难点.不同的调用方式,会使this指向不同的对象.而使用call,apply,bind等方式,可改变this的指向,完成一些令人惊叹的黑魔法 ...

  8. OpenResty+缓存穿透基本介绍

    OpenResty                1.lua表达式                 2.Nginx的安装                3.OpenResty              ...

  9. (转)python中join()方法

    原文:http://blog.csdn.net/weixin_40475396/article/details/78227747 函数:string.join() Python中有join()和os. ...

  10. 录音文件lame转换MP3相关配置

    文件下载整个功能完成了,那么对应的文件上传也跑不了.So~ Look here~ 业务需求是录制音频然后上传到七牛并且Android可以读. 与安卓沟通了一下统一了mp3格式,大小质量都不错.由于AV ...