深入了解Objective-C 和 Core Foundation 对象相互转换(_bridge)

来源:互联网 发布:js 触发div事件 编辑:程序博客网 时间:2024/06/07 12:48

介绍Core Foundation框架

Core Foundation是使用C实现的,它们为iOS应用程序提供基本数据管理和服务功能;

下面列举该框架支持进行管理的数据以及可提供的服务:
1、群体数据类型 (数组、集合等)
2、程序包
3、字符串管理
4、日期和时间管理
5、原始数据块管理
6、偏好管理
7、URL及数据流操作
8、线程和RunLoop
9、端口和soket通讯

特别注意的是:
针对内存管理问题,ARC 可以帮忙管理 Objective-C 对象, 但是不支持Core Foundation 对象的管理,所以转换后要注意一个问题:谁来释放使用后的对象。

二者之间联系

他们虽然属于不同的 Framework,但是具有相同的对象结构,所以可以用标准C的类型转换。

Foundation 对象和 Core Foundation对象间的转换:俗称桥接 bridge

Core Foundation框架和Foundation框架紧密相关,它们为相同功能提供接口,但Foundation框架提供Objective-C接口。

如果您将Foundation对象和Core Foundation类型掺杂使用,则可利用两个框架之间的 “toll-free bridging”。

所谓的Toll-free bridging是说您可以在某个框架的方法或函数同时使用Core FoundatioFoundation 框架中的某些类型。

很多数据类型支持这一特性,其中包括群体和字符串数据类型。
每个框架的类和类型描述都会对某个对象是否为 toll-free bridged,应和什么对象桥接进行说明。
如需进一步信息,请阅读Core Foundation框架参考。

非ARC环境下的转换

本项目是ARC环境下的,创建一个非ARC的BridgeMRCNSObject文件。

#import "BridgeMRC.h"@implementation BridgeMRC- (void)forExample{    NSString *aNNstring = @"hello world";    CFStringRef aCFString = (CFStringRef)aNNstring;    NSString *aString = (NSString *)aCFString;    NSLog(@"%@",aString);    [aNNstring release];}@end

我们在控制台看下结果:

我们会发现,真的只是单纯的转化,地址并没有变。
其实不测试也会是如此,毕竟没有new,只是转化而已。

最后不要忘记这是MRC环境下,要手动release

ARC环境下的转换

ARC的诞生大大简化了我们针对内存管理的开发工作,但是只支持管理 Objective-C 对象, 不支持 Core Foundation 对象。
Core Foundation 对象必须使用CFRetainCFRelease来进行内存管理。

那么当使用Objective-CCore Foundation对象相互转换的时候,必须让编译器知道,到底由谁来负责释放对象,是否交给ARC处理。只有正确的处理,才能避免内存泄漏和double free导致程序崩溃。

根据不同需求,有3种转换方式:__bridge                   (不改变对象所有权)__bridge_retained 或者 CFBridgingRetain()               (解除 ARC 所有权)__bridge_transfer 或者 CFBridgingRelease()             (给予 ARC 所有权)
原文是这么写的:A bridged cast is a C-style cast annotated with one of three keywords:(__bridge T) op casts the operand to the destination type T. If T is a retainable object pointer type, then op must have a non-retainable pointer type. If T is a non-retainable pointer type, then op must have a retainable object pointer type. Otherwise the cast is ill-formed. There is no transfer of ownership, and ARC inserts no retain operations.(__bridge_retained T) op casts the operand, which must have retainable object pointer type, to the destination type, which must be a non-retainable pointer type. ARC retains the value, subject to the usual optimizations on local values, and the recipient is responsible for balancing that +1.(__bridge_transfer T) op casts the operand, which must have non-retainable pointer type, to the destination type, which must be a retainable object pointer type. ARC will release the value at the end of the enclosing full-expression, subject to the usual optimizations on local values.These casts are required in order to transfer objects in and out of ARC control; see the rationale in the section on conversion of retainable object pointers.Using a __bridge_retained or __bridge_transfer cast purely to convince ARC to emit an unbalanced retain or release, respectively, is poor form.

文档传送门:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#bridged-casts

1、__bridge

__bridge 只做类型转换,不改变对象所有权,是我们最常用的转换符。

我们通过泛型稍微了解一下,然后通过具体的NSString类型来详细解谈。

在MRC中,二者可以互相任意的转换使用。

    id obj = [[NSObject alloc] init];    void *p = obj;    id obj1= p;    [obj1 release];

在ARC中,需要进行bridge

    id obj = [[NSObject alloc] init];    void *p = (__bridge void *)(obj);    id obj1= (__bridge id)(p);

Objective-C的对象类型用 __bridge 转换为void*类型和使用__unsafe_unretained 关键字修饰的变量是一样的。

特别主要的是,被代入对象的所有者需要明确对象生命周期的管理,不要出现异常访问的问题。

1.1、从OC转CF,ARC管理内存

- (void)forExample{    NSString *aNNstring = [NSString stringWithFormat:@"%@",@"hello world"];    CFStringRef aCFString = (__bridge CFStringRef)aNNstring;    NSLog(@"Retain count is %ld", CFGetRetainCount(aCFString));}

我们使用Analyze搞一下,没问题,说明确实是ARC来管理内存的。

1.2、 从CF转OC,需要开发者手动释放,不归ARC管

我们看下下面的例子:

- (void)forExample{    CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);    NSString *aNSString = (__bridge NSString *)aCFString;    NSLog(@"%@",aNSString);    CFRelease(aCFString);}

我们屏蔽掉代码CFRelease(aCFString);,然后再Analyze搞一下

内存泄露了。还是需要我们乖乖的手动进行内存管理的。

2、 __bridge_retained 或者 CFBridgingRetain()

__bridge_retained 或者CFBridgingRetain()Objective-C对象转换为Core Foundation对象,把对象所有权桥接给Core Foundation对象,同时剥夺ARC的管理权,后续需要开发者使用CFRelease或者相关方法手动来释放对象。

例子1:对象指针角度

    void *p = 0;    {        id obj = [[NSObject alloc] init];        p = (__bridge_retained void *)obj;    }    NSLog(@"class=%@", [(__bridge id)p class]);

运行后,我们可以看到结果

2017-08-10 17:14:07.095 1111111111[26587:539032] class=NSObject

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

例子2:内存管理例子

【泛型例子】

在MRC环境下,

    id obj = [[NSObject alloc] init];    void *p = obj;    [(id)p retain];

在ARC环境下,

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

【具体对象例子】

- (void)forExample{    NSString *aNNstring = [NSString stringWithFormat:@"%@",@"hello world"];    CFStringRef aCFString = (__bridge_retained CFStringRef)aNNstring;    CFRelease(aCFString);    NSLog(@"Retain count is %ld", CFGetRetainCount(aCFString));}

我们屏蔽掉代码CFRelease(aCFString);,然后再Analyze搞一下

内存泄露了。还是需要我们乖乖的手动进行内存管理的。

Ps:

CFBridgingRetain()__bridge_retained的宏方法,在Core Foundation内部,提供了以下函数:

CFTypeRef  CFBridgingRetain(id  X)  {      return  (__bridge_retained  CFTypeRef)X;  }  

下面两行代码等价:

CFStringRef aCFString = (__bridge_retained CFStringRef) aNSString;
CFStringRef aCFString = (CFStringRef) CFBridgingRetain(aNSString);

3、 __bridge_transfer 或者 CFBridgingRelease()

__bridge_transfer 或者CFBridgingRelease() 将非Objective-C对象转换为Objective-C对象,同时将对象的管理权交给ARC,开发者无需手动管理内存。

【泛型例子】

在MRC下,

    id obj = [[NSObject alloc] init];    void *p = obj;    [(id)p retain];    // p 变量原先持有对象的所有权    id obj2 = (id)p;    [obj2 retain];    [(id)p release];    [obj2 release];    [obj release];

在ARC下,

    id obj = [[NSObject alloc] init];    void *p = (__bridge_retained void *)(obj);    id obj2 = (__bridge_transfer id)(p);

说明在MRC环境下,我们是需要手动进行内存管理的。

在ARC环境下,使用__bridge_retained__bridge_transfer 相当于自动的进行了retain release 内存管理操作。

使用__bridge_transfer,内存管理就交给了ARC进行处理。

【具体对象例子】

- (void)forExample{    NSString *aNNstring = [NSString stringWithFormat:@"%@",@"hello world"];    CFStringRef aCFString = (__bridge_retained CFStringRef)aNNstring;    NSString *bNNstring = (__bridge_transfer NSString *)(aCFString);    NSLog(@"%@",bNNstring);}

Analyze搞一下,没问题。

CFBridgingRelease()__bridge_transfer的宏方法,在Core Foundation内部,提供了以下函数:

id  CFBridgingRelease(CFTypeRef  X)  {      return  (__bridge_transfer  id)X;  }  

下面两行代码等价:

aNSString = (__bridge_transfer NSString *)aCFString;
aNSString = (NSString *)CFBridgingRelease(aCFString);

Toll-Free bridged(免费的桥接)

关于Toll-Free Bridged Types:
https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html

在iOS世界,主要有两种对象:objective-c对象和Core Foundation对象0。

Core Foundation 对象主要是有c语言实现的 Core Foundation Framework 的对象,其中也有对象引用计数的概念,只是不是使用Cocoa Framework or Foundation Frameworkretain/release,而是自身的 CFRetain/CFRelease 接口。

这两种对象间可以互相转换和操作,不使用ARC的时候,单纯的用C原因的类型转换,不需要消耗CPU的资源,所以叫做 Toll-Free bridged(免费的桥接)。

比如 NSArrayCFArrayRef, NSStringCFStringRef,他们虽然属于不同的 Framework,但是具有相同的对象结构,所以可以用标准C的类型转换。

正因为Objective-C是ARC管理的对象,而Core Foundation不是ARC管理的对象,所以才要特意这样转换,这与id类型向void*转换是一个概念。也就是说,当这两种类型(有ARC管理,没有ARC管理)在转换时,需要告诉编译器怎样处理对象的所有权。

上面的__bridge_retained__bridge_transfer以及_bridge就是做了这个事情。

推荐阅读

ARC中的bridge介绍
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#bridged-casts

Objective-C 和 Core Foundation 对象相互转换
http://www.cnblogs.com/caolongs/p/7083846.html

[IOS 开发] __bridge、__bridge_transfer和__bridge_retained详解
http://blog.csdn.net/u010130947/article/details/44493931

Toll-Free Bridged Types
https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html

[iOS]深入理解__bridge - OC对象与C++对象的引用转换
https://yq.aliyun.com/articles/58964

网友问答
http://cocoadventures.org/post/126892893629/understanding-bridged-casts

https://stackoverflow.com/questions/14854521/where-and-how-to-bridge

阅读全文
0 0
原创粉丝点击