Toll-Free Bridging详解——CoreFoundation和Foundation框架的无损桥接

    我们知道OC是C语言的超集,也就是说Objective-C是对C的扩充,正确理解来说就是OC是用C语言写成的运行库。Objective-C是C语言的严格超集,任何C语言程序不经修改就可以直接通过Objective-C编译器,在Objective-C中使用C语言代码也是完全合法的。Objective-C被描述为盖在C语言上的薄薄一层,因为Objective-C的原意就是在C语言主体上加入面向对象的特性。

    我们开始研究Toll-Free Bridging之前就要有对前面这段话有一定的,接下来我们解释起来就容易的多了。我们知道OC没有命名空间,但是有许多前缀,NS、UI、CF……关于前缀我们不过多的解释,我们这里提一下CF,Core Foundation框架CoreFoundation.framework是一组C语言接口,它们为应用程序提供基本数据管理和服务功能。

    如果有朋友暂时想不起来CF的东西,我们大可以回想一下我们在开发过程中经常碰到崩溃信息不是NS……而是“CF……”的错误,那其实就是CF框架下的东西。我们这里记住,CF前缀的一般是提供底层功能的就暂时够了。好了,偏低层的交给我们的CoreFoundation框架,那我们不底层的用什么?比如我们开发经常用到的数组,CoreFoundation type是 CFArrayRef,Foundation class是NSArray,有什么区别呢?其实有许多数据类型在核心基础框架(CoreFoundation)和基础框架(Foundation)可以互换使用。这种能力称为无损桥接,意味着您可以使用相同的数据类型作为参数函数调用核心基础或作为一个objective - c消息的接收者。官方给出的是NSLocale和CFLocale,但是我们初学者很少用,所以我们贴出两段代码来理解,初学者先看NSArray和CFArray,开发经验较丰富的可以看下面关于NSlocale的代码段。

     我们看一下下面这部分代码(注意引入CoreFoundation框架):

    NSArray *_array = [[NSArray alloc]init];
    NSString *values[] = {@"foo",@"bar"};
    CFArrayRef arrayRef = CFArrayCreate(kCFAllocatorDefault, (void *)values, (CFIndex)2,NULL);
    NSArray *array = (__bridgeNSArray *)arrayRef;//注意这一行的__bridge
    NSData *data = [NSKeyedArchiverarchivedDataWithRootObject:array];

   

       上面的代码我们实现了什么?实现了NSArrayCFArrayRef的互相转化,其中有注释的哪一行我们发现一个关键词__bridge”,这是在ARC下转化需要默认加上的关键字,告诉编译器持有者。下面的代码我们就用MRC,因此不需要加__bridge(稍后我们提到关于内存的管理)。

          NSlocale,作为大家都不常用的一个类,我先来简单介绍一下给初学者,比如切换应用的语言(新浪微博、微信都是可以换成英文的哦)等等会用到这个类。NSLocale类是将与国家和语言相关的信息进行简单的组合,包括货币,文学方面的信息。货币:货币的国际名称(人民币的国际货币名称是CNY);货币符号(人民币的国际货币符号是¥)、文学:标点符号,文字的书写顺序(左右顺序),引用的起止符号等等、若做金融一类的应用或者市场较广泛的应用可能会用到NSLocale这个类,它是一个区域类这是题外话,我们回到主线来研究代码。

        我们可以看一下更为官方的代码示例:  

<pre name="code" class="objc">    NSLocale *gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
    CFLocaleRef gbCFLocale = (CFLocaleRef) gbNSLocale;
    CFStringRef cfIdentifier = CFLocaleGetIdentifier (gbCFLocale);
    NSLog(@"cfIdentifier: %@", (NSString *)cfIdentifier);
    // logs: "cfIdentifier: en_GB"
    CFRelease((CFLocaleRef) gbNSLocale);
    
    CFLocaleRef myCFLocale = CFLocaleCopyCurrent();
    NSLocale * myNSLocale = (NSLocale *) myCFLocale;
    [myNSLocale autorelease];
    NSString *nsIdentifier = [myNSLocale localeIdentifier];
    CFShow((CFStringRef) [@"nsIdentifier: " stringByAppendingString:nsIdentifier]);
    // logs identifier for current locale
 

        我们仔细阅读完这两段代码并且运行以后,我相信大部分人通过阅读这两段代码,就明白了怎么回事。我来大白话说一下,就是CoreFoundation框架是底层框架,但是我们学习OC大部分时间用的是Foundation框架,前者对于数据的处理是type,后者对于数据的处理是class,两者在功能上的区别是没有的。既然能互相转换,那就证明我们完全相等的去使用这两者,其实实际上我们使用苹果给我们封装好的Foundation框架就可以,上手方便,代码熟练,但是之所以我们还时不时的使用CoreFoundation的东西是因为某些底层API,如CoreGraphics和 CoreText,都基于CoreFoundation,而且不太可能会有Objective-C的版本。幸运的是, iOS的设计使得这两种类型的对象非常容易转换。例如NSString和 CFStringRef就可以同等对待,在任何地方都可以互换使用,背后的设计就是Toll-Free Bridging。

    我相信,介绍到这里,基本上大家都懂了什么是Toll-Free Bridging,也让我们离底层更近了一步。所谓的OC中的Toll-Free Bridging就是用来很完美的连通两个框架,我们编程上称之为“无损桥接”。

    有一些数据类型是能够在 Core Foundation Framework 和 Foundation Framework 之间交换使用的。这意味着,对于同一个数据类型,你既可以将其作为参数传入 Core Foundation 函数,也可以将其作为接收者对其发送 Objective-C 消息(即调用ObjC类方法)。这种在 Core Foundation 和 Foundation 之间交换使用数据类型的技术就叫 Toll-Free Bridging。

    

    不过我们要理解一个东西就是Foundation框架下,Objective-C对象是继承于NSObject的对象,而CoreFoundation框架下,对象是指由C语言中struct定义的各种对象。最后我们附上一段关于字符串互相转化的代码,帮助大家理解:

    

    这是Objective-C的对象转化成CF框架下的对象:

   // ARC 环境下 ObjC to CF
    NSString *foo = @"bar";
    CFStringRef bar = (__bridge CFStringRef)(foo);
    NSLog(@"%ld", CFStringGetLength(bar));

     这是CF框架下的对象转化为Objective-C对象:  

    //ARC 环境下   CF to ObjC
    CFStringRef foo = CFStringCreateWithCString(kCFAllocatorDefault, "foo", kCFStringEncodingUTF8);
    NSString *bar = (__bridge NSString *)(foo);
    NSLog(@"%ld", bar.length);
    CFRelease(foo);

而关于Toll-Free Bridging这种技术的内部实现——类簇,我会在下一片博文中细讲。今天我们先了解他的作用和意义,以及下面的注意事项。


    但是我们知道有一个东西叫“内存管理”……,前面我们只说了__bridge,我们还需要注意一些地方。如果在MRC下,上面的那个例子需要说明的是,内存管理的函数和方法是可以互换的,也就是说你可以对一个Cocoa对象进行CFRelease,也可以对一个CoreFoundation对象使用autorelease或者release。MRC 下的 Toll-Free Bridging因为不涉及内存管理的转移,相互之间可以直接交换使用。

    

    而在 ARC 下,事情就会变得复杂一些,因为 ARC 能够管理 Objective-C 对象的内存,却不能管理 CF 对象,CF 对象依然需要我们手动管理内存。在 CF 和 ObjC 之间 bridge 对象的时候,问题就出现了,编译器不知道该如何处理这个同时有 ObjC 指针和 CFTypeRef 指向的对象。这时候,我们需要使用__bridge,__bridge_retained,__bridge_transfer 修饰符来告诉编译器该如何去做。多数情况下,Objective-C 对象和 Core Foundation 对象之间互相转换时, 我们都应该使用__bridge。但是有时候我们确实需要给予 ARC 某个对象的所有权, 或者解除 ARC 对某个对象的所有权。这种情况下我们就需要使用另外两种 bridging casts:

    

   __bridge_transfer:给予 ARC所有权

    

   __bridge_retained:解除 ARC所有权

      

关于内存管理的介绍,大家可以自己查阅,本文不以此为主,如有纰漏差错,欢迎讨论。附表是所有支持Toll-Free Bridging技术的对照表,希望对大家有帮助。

    

    

   注意:不是所有的数据类型都是无损桥接,即使它们的的名字很相似。例如,NSRunLoop和CFrunLoop,NSBundle和CFBundle,NSDateFormatter和CFDateformatter。


       







  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值