OC与CF互换:Toll-Free-Bridging

Bridge概述

在了解Toll-Free-Bridging之前,要先知道两个重要的Framework:Core Foundation跟Foundation。

Core Foundation框架 (CoreFoundation.framework) 是一组C语言接口,它们为iOS应用程序提供基本数据管理和服务功能。Core Foundation拥有更久的使用历史,并且Core Foundation在某种程度上为Foundation提供支持。Foundation作为Cocoa层的高级框架,采用Objective-C编程。

在命名上一般可以区分CoreFoundation跟Foundation类,CoreFoundation类一般是CF前缀,如CFStringRef;Foundation一般是NS前缀,如NSString。

因为CoreFoundation与Foundation的相关性,部分数据结构的CoreFoundation跟Foundation可以相互转换使用,这个转换过程就叫做Toll-Free-Bridging。

并不是所有CoreFoundation与Foundation都能进行Toll-Free-Bridging。Apple官网列出了详细的类型:Apple官网链接。下图示意了目前转换的类型:

MRC与ARC下的bridge

Toll-Free-Bridging在ARC与MRC的情况下表现并不一样。MRC因为与CoreFoundation数据结构一样是进行手动内存管理的,所以直接进行类型的强制转换即可。而ARC是自动内存管理,需要注意的细节比较多。

MRC下的Bridge:

1
2
3
4
5
6
7
8
9
10
11
// bridge 
NSString *nsStr = (NSString *)cfStr;
CFStringRef cfStr = (CFStringRef)nsStr;

// 调用函数或者方法
NSUInteger length = [(NSString *)cfStr length];
NSUInteger length = CFStringGetLength((CFStringRef)nsStr);

// release
CFRelease((CFStringRef)nsStr);
[(NSString *)cfStr release];

而在 ARC 下,事情会比MRC复杂一些,因为 ARC 能够管理 Objective-C 对象的内存,却不能管理 CF 对象,CF 对象依然需要我们手动管理内存。在 CF 和 ObjC 之间 bridge 对象的时候,问题就出现了,编译器不知道该如何处理这个同时有 ObjC 指针和 CFTypeRef 指向的对象。

ARC下的bridge:

在ARC情况下进行bridge可以有3种修饰符来处理:bridge, bridge_retained, __bridge_transfer。

__bridge

这意味着告诉编译器不做任何内存管理的事情,编译器仍然负责管理好在 Objc 一端的引用计数的事情,开发者也继续负责管理好在 CF 一端的事情。

OC对象转CF对象:

1
2
3
4
// objc to cf 
NSString *nsStr = [self createSomeNSString];
CFStringRef cfStr = (__bridge CFStringRef)nsStr;
CFUseCFString(cfStr);

nsStr通过__bridge强制转换为cfStr后,nsStr并没有被retain,nsStr按照正常的ARC进行管理,cfStr也不需要进行手动的release操作。但是需要注意的是当nsStr被释放的时候,因为没有retain,同时表示cfStr指向的对象被释放,这时再使用cfStr将报错。

CF转OC对象:

1
2
3
4
5
// cf to objc 
CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
NSString *world = (__bridge NSString *)(hello);
CFRelease(hello);
[self useNSString:world];

bridge时不会进行任何内存管理的事情,bridge之后,编译器会对OC对象进行ARC,但不会处理CF对象。这时就要手动对CF对象进行内存管理。

__bridge_retained

仅用bridge修饰的话,OC转CF可能会出现崩溃问题。这个时候如果使用bridge_retained的话,可以避免该问题。使用__bridge_retained,在 bridge 的时候,编译器会 retain 对象,而由开发者在CF一端负责 release。这样,就算nsStr在 objc 一端被释放,cfStr指向的对象并没有释放。这时,开发者需要保证和负责CF对象的释放。

例如:

1
2
3
4
5
// objc to cf 
NSString *nsStr = [self createSomeNSString];
CFStringRef cfStr = (__bridge_retained CFStringRef)nsStr;
CFUseCFString(cfStr);
CFRelease(cfStr);

__bridge_transfer

上面的bridge_retained帮开发者在OC对象转CF对象的时候进行了retain操作。而bridge_transfer可以帮我们进行CF对象转OC对象的release的操作。

根据前面bridge修饰符进行CF强制转OC,可以发现此时是要进行手动地release的。如果用bridge_transfer修饰符,就可以不用手动release。

例如:

1
2
3
4
// cf to objc 
CFStringRef hello = CFStringCreateWithCString(kCFAllocatorDefault, "hello", kCFStringEncodingUTF8);
NSString *world = (__bridge_transfer NSString *)(hello);
[self useNSString:world];

三种修饰都介绍完了。我们可以总结下。

  1. __bridge:默认形式的OC对象跟CF对象相互转换,bridging时不作任何处理。
  2. __bridge_retained:在OC转CF对象bridging时,对CF对象进行了retain处理。
  3. __bridge_transfer:在CF转OC对象bridging时,对CF对象进行了release处理。
     

参考:

http://www.cocoachina.com/industry/20140425/8235.html

http://blog.csdn.net/sanpintian/article/details/8139878