结合 category 工作原理分析 OC2.0 中的 runtime

421 查看

绝大多数 iOS 开发者在学习 runtime 时都阅读过 runtime.h 文件中的这段代码:

可以看到其中保存了类的实例变量,方法列表等信息。

不知道有多少读者思考过 OBJC2_UNAVAILABLE 意味着什么。其实早在 2006 年,苹果在 WWDC 大会上就发布了 Objective-C 2.0,其中的改动包括 Max OS X 平台上的垃圾回收机制(现已废弃),runtime 性能优化等。

这意味着上述代码,以及任何带有 OBJC2_UNAVAILABLE 标记的内容,都已经在 2006 年就永远的告别了我们,只停留在历史的文档中。

Category 的原理

虽然上述代码已经过时,但仍具备一定的参考意义,比如 methodLists 作为一个二级指针,其中每个元素都是一个数组,数组中的每个元素则是一个方法。

接下来就介绍一下 category 的工作原理,在美团的技术博客 深入理解Objective-C:Category 中已经有了非常详细的解释,然而可能由于时间问题,其中的不少内容已经过时,我根据目前最新的版本(objc-680) 做一些简单的分析,为了便于阅读,在不影响代码逻辑的前提下有可能删除部分无关紧要的内容。

概述

首先 runtime 依赖于 dyld 动态加载,在 objc-os.mm 文件中可以找到入口,它的调用栈简单整理如下:

以上四个方法可以理解为 runtime 的初始化过程,我们暂且不深究。在 _read_images 方法中有如下代码:

根据注释可见苹果曾经计划利用 category 来添加属性。在 addUnattachedCategoryForClass 方法中会找到当前类的所有 category,然后在 remethodizeClass 真正的去做处理。不过到目前为止还没有接触到相关的 category 处理,我们继续沿着调用栈向下走:

这里的 attachCategories 就是处理 category 的核心所在,不过在阅读这段代码之前,我们有必要先熟悉一下相关的数据结构。

Category 相关的数据结构

首先来了解一下一个 Category 是如何存储的,在 objc-runtime-new.h 中可以看到如下定义,我只列出了其中成员变量:

可见一个 category 持有了一个 method_list_t 类型的数组,method_list_t 又继承自 entsize_list_tt,这是一种泛型容器: