ARC中__bridge, __bridge__transfer, __bridge_retained 关系

现在已经凌晨了,但是我却没发觉困。

琢磨了好久的东西,终于顿悟。

这篇是其中的一个点,记录下来,以备后续查阅!

说到__bridge,就不得不说Objective-C和Core Foundation对象之间的关系。

当你在 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;

	[s3 release];

return [(NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,(CFStringRef)text,NULL,(CFStringRef)@"!*‘();:@&=+$,/?%#[]",

CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))autorelease];// 这里不需要 bridgingcasts,因为这是
    一个常量,不需要释放!

}

首先需要移除 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(NSUTF8StringEncoding)));

    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,表示临时使用某种类型,不改变对象的所有权。
时间: 2024-10-05 23:26:56

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

ios 中__bridge,__bridge_transfer和__bridge_retained详解

转载自:http://www.cocoachina.com/industry/20130411/5975.html Objective-C和Core Foundation 对象相互转换的内存管理总结 发布于:2013-04-11 13:37阅读数:4109 iOS允许Objective-C 和 Core Foundation 对象之间可以轻松的转换,拿 NSString 和 CFStringRef 来说,直接转换毫无压力: [cpp] view plaincopyprint? 01. CFStr

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

ARC中block块作为属性的使用笔记 block较难理解,根据在内存中的分布情况就分为3种类型,根据使用的情形又分为很多很多种.虽然用起来容易,但使用不当会造成内存泄露,虽然都是这么说,但你真的研究过为什么会泄露吗?为什么有些时候外部变量进入block的时候会导致引用计数+1呢? 本人做过MRC以及ARC的开发,但大势所趋,ARC将是以后开发的主要模式,即使有MRC也是ARC混编MRC的代码,所以,本文的block的一些使用上的心得都基于ARC的,已经不考虑MRC的了,请看官注意,MRC与AR

【转】UML中的几种关系详细解析

UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现 类与类图 1) 类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性.操作.关系的对象集合的总称. 2) 在系统中,每个类具有一定的职责,职责指的是类所担任的任务,即类要完成什么样的功能,要承担什么样的义务.一个类可以有多种职责,设计得好的类一般只有一种职责,在定义类的时候,将类的职责分解成为类的属性和操作(即方法). 3) 类的属性即类的数据职责,类的操作即类的行为职责 一.依赖关系(Dependence) 依

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

引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题 : [小测试]你真的知道blocks在Objective-C中是怎么工作的吗?,发现竟然做错了几道, 才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客. Block简介(copy一段) Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事.需要注意的是由于Objective-C在iOS中不支

UML中的6大关系详细说明

UML中的6大关系详细说明: 1.关联关系: 含义:类与类之间的连结,关联关系使一个类知道另外一个类的属性和方法:通常含有"知道","了解"的含义 体现:在C#中,关联关系是通过成员变量来实现的: 方向:双向或单向: 图示:实线 + 箭头:箭头指向被关联的类:2.依赖关系: 含义:是类与类之间的连接,表示一个类依赖于另外一个类的定义:依赖关系仅仅描述了类与类之间的一种使用与被使用的关系: 体现:在C#中体现为局部变量.方法/函数的参数或者是对静态方法的调用: 方向:

Hibernate中的一对一映射关系

Hibernate中的一对一映射关系有两种实现方法(一对一关系:例如一个department只能有一个manager) I使用外键的方式 步骤:1在一对一关联的持久化类中互相添加对方对象属性,   例如在department中添加private manager manager属性:   department代码如下: package com.atguigu.hibernate.one2one.foreign; public class Department { private Integer d

OSGI中的service依赖关系管理

众所周知,对于高动态高可扩展的应用,OSGI是一个非常好的平台.但是,也因此增加了复杂性,开发中对service的依赖变得复杂.这也是service的关系管理成为OSGI中一个非常重要的部分,我们来看看OSGI中service依赖关系管理的方式.篇幅原因,只关注发展历程,不具体介绍每个方式的详细实现细节. 概括的说,目前在OSGI中主要有以下几种service依赖关系管理的方法: 1. Service listener 2. Service binder 3. Dependency Manage

angularjs 中的scope继承关系——(2)

转自:http://www.lovelucy.info/understanding-scopes-in-angularjs.html angularjs 中的scope继承关系 ng-include 假设在我们的 controller 中, $scope.myPrimitive = 50; $scope.myObject = {aNumber: 11}; HTML 为: <script type="text/ng-template" id="/tpl1.html&quo

AVPicture中data与linesize关系

AVPicture中data与linesize关系 分类: FFMPEG2010-12-31 20:30 2481人阅读 评论(3) 收藏 举报 filter测试存储 AVPicture结构中data和linesize关系 AVPicture里面有data[4]和linesize[4]其中data是一个指向指针的指针(二级.二维指针),也就是指向视频数据缓冲区的首地址,而data[0]~data[3]是一级指针,可以用如下的图来表示: data -->xxxxxxxxxxxxxxxxxxxxxx