Friday Q&A Toll Free Bridging Internals by Mike Ash

https://www.mikeash.com/pyblog/friday-qa-2010-01-22-toll-free-bridging-internals.html

又过了一周,现在是周五的问答时间。这周的主题是toll-free bridging,由Jonathan Mitchell提问。

What It Is

我希望所有的读者已经知道什么是toll-free bridging,但是如果你不知道,这里有个总结。

Toll-free bridging,简称为TFB,是一种允许objective-c类和corefoundation类互相转换的机制。举个例子,NSString 和 CFString 被绑定,这意味着你可以将任何NSString看做CFString反之亦然。例子:

CFStringRef cfStr = SomeFunctionThatReturnsCFString();

NSUInteger length = [(NSString *)cfStr length];


大多数同时存在Cocoa和CoreFoundation的(但不是全部)类通过这样的方式关联。在这篇文章中将通过一个bridged类来阐述bridging。

Bridging From CF to ObjC

将一个CoreFoundation类关联到Objective-C的方法十分地简单。

每一个关联的类其实一个类簇,这意味这公开的接口类是一个虚类,实际的实用功能由私有的子类实现。CoreFoundation类是这些子类中的一个,与Objective-C对应的CoreFoundation类。其它的Objective-C类也可以独立存在。从外部看,他么都看起来一样,因为他们都有着共同的接口。

来看个具体的例子NSString。NSString是一个虚类。每一次你创建这个类时,你实际上获得的是它的一个子类实例。

这些子类中的一个是NSCFString。它是直接对应CFString的副本。CFString的开头是一个isa指针指向NSCFString类,这让它可以像一个Objective-C对象一样工作。

NSCFString实现NSString的方法来工作。有两种可能的方法。一种方法是通过封装CoreFoundation的方法。另一种方法是自行实现对应的方法。事实上,代码的实现可能是这两种方法的结合。

从CF到ObjC这个方向来看,绑定的机制很简单甚至根本不存在。CFString对象恰巧是NSString类的子类NSCFString的一个实例。许多实现只是碰巧调用CoreFoundation来让他们工作。

Bridging from ObjC to CF另一个方向上的关联稍微复杂。这是因为需要转换的可能是任何一种TFB Objective-C类实例,甚至用户自己创建的类。你只需要写一个NSString的子类,你就有了这样一个类。同时这些自定义的类仍然可以对CoreFoundation函数透明的工作。你可以对你的自定义子类调用callCFStringGetLength,它将调用你的length方法并返回。

正如看到的,并没有什么特殊的魔法来让其正常工作。只是简单地进行了转换。CFStringGetLength看起来像这样

CFIndex CFStringGetLength(CFStringRef str) {

        CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFIndex, str, "length");

    

        __CFAssertIsString(str);

        return __CFStrLength(str);

}

第一行是一个丑陋的宏转换它隐藏着TFB如何工作。它检查对象的isa是否是一个NSCFString。如果不是,它将不是真正的CFString,而是其它的某个Objective-C类。这种情况时,CoreFoundation并不知道如何查询length,因此它只是将消息发送给对象并将结果返回。如果它是一个“真的”CFString,它只要简单地调用__CFStrLength。

简单来说:每一个TFB的CoreFoundation函数首先检查被传递进来的对象是不是一个“真的”CoreFoundation对象还是一个纯的Objcetive-C对象。如果它是一个纯的Objective-C,它简单的将信号传递给Objcetive-C。另一种情况,它进行正常地处理。这就是为什么我说它进行了简单的转换:每一个函数方法在开头都进行这样的检测来确保TFB正常工作。

这样的实现导致了一个有趣结果。假设你碰巧将一个CFArray传递给了CFStringGetLength。isa检查发现它不是一个NSCFString,所以它像CFArray传递信号,结果是你得到一个这样的错误:

-[NSCFArray length]: unrecognized selector sent to instance 0x100108e50

从CoreFoundation返回了Objcetive的错误。


Bridging Basic Behavior

显示关联通过这种方式工作。但TFB还有一个有趣的部分:所有的对象和类具有一样的基本行为。本质上,NSObject关联到CFType。作为最普遍的例子是,Objective-C对象可以应用CFRetainany,CoreFoundation对象可以应用retain。和其它关联一样,如果在Objective-C子类中你重写了retain,CFRetain将调用被重载的方法。这不仅仅适用于内存管理,同样适用所有的CFType函数,如CFCopyDescription;以及所有的NSObject方法,如performSelector:withObject:afterDelay:

为了关联到Objective-C,任意的CoreFoundation对象的起始部分指向Objective-C类。在Objectove-C中有相关联类的CoreFoundation类指向关联的类,对于没有的则指向一个特殊的__NSCFType类。所有这些类都是NSObject的子类,他们天生就具有CoreFoundation中对应类的行为。对应CoreFoundation中的方法,他们可能重写或者直接调用CoreFoundation的方法进行封装。

为了关联到CoreFoundation,将进行特别的绑定。CFRetain和其它CF类型的函数的首行将检查对象是不是一个“真的”CF对象。如果是,它执行正常的操作,如果不是它将信号分发给对象的Objective-C函数进行工作。

Creating Bridged Classes

我希望这一部分的标题没有给大家带来希望,因为你无法实现这样的工作。现在我们知道了关联是如何工作的,可以很明显的知道原因。你不能关联一个不存在的CF类因为这需要在CF方进行大量的协同工作。每一个方法需要在起始处检查传递进来的对象,你不能在CF方法中添加原本不存在的东西。你不能创建新的CF类,因为apple没有将其开放。

Conclusion

Now you know the basics of how toll-free bridging works. If you're interested in the deeper technical details of just what the dispatching code looks like and how it works, check out ridiculous_fish's article on bridging.

That brings this week's edition to a close, but come back next week for yet another one. Friday Q&A is, of course, driven by user submissions. If you have an idea you would like to see covered in this space, send it in!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值