前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS RunTime之六:Category

iOS RunTime之六:Category

作者头像
s_在路上
发布2018-09-11 17:09:48
6020
发布2018-09-11 17:09:48
举报
文章被收录于专栏:iOS 开发杂谈iOS 开发杂谈

CategoryObjective-C 2.0 之后添加的特性,一般我们使用 Category 的场景主要可以动态地为已经存在的类扩展新的属性和方法。这样做的好处就是:

  • 可以减少臃肿的代码。
  • 可以把不同的功能拆开,方便以后的维护。

runtime.h中查看定义中:

代码语言:javascript
复制
typedef struct objc_category *Category;

同样也是一个 objc_category 结构体,定义如下:

代码语言:javascript
复制
struct objc_category {
    char *category_name                                      OBJC2_UNAVAILABLE;
    char *class_name                                         OBJC2_UNAVAILABLE;
    struct objc_method_list *instance_methods                OBJC2_UNAVAILABLE;
    struct objc_method_list *class_methods                   OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

打开 objc 源代码,在objc-runtime-new.h中我们可以发现:

代码语言:javascript
复制
struct category_t { 
    const char *name; 
    classref_t cls; 
    struct method_list_t *instanceMethods; 
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
};
代码语言:javascript
复制
name:是指 class_name 而不是 category_name。
cls:要扩展的类对象,编译期间是不会定义的,而是在Runtime阶段通过name对应到对应的类对象。
instanceMethods:category中所有给类添加的实例方法的列表。
classMethods:category中所有添加的类方法的列表。
protocols:category实现的所有协议的列表。
instanceProperties:表示Category里所有的properties,这就是我们可以通过objc_setAssociatedObject和objc_getAssociatedObject增加实例变量的原因。

注意:

  • OBJC2_UNAVAILABLE 之类的宏定义是苹果在 OC 中对系统运行版本进行约束的黑魔法,为的是兼容非 `Objective-C 2.0 的遗留逻辑,但我们仍能从中获得一些有价值的信息,有兴趣的可以查看源代码。
  • 从上面的 category_t 的结构体中可以看出,分类中可以添加实例方法,类方法,甚至可以添加协议,添加属性,但是不可以添加成员变量。

下面看一个例子

Paste_Image.png

使用 clang 的命令去看看 category 到底会变成什么:

代码语言:javascript
复制
clang -rewrite-objc Person+Student.m

注意:

Paste_Image.png

解决方法 在终端中输入下面的命令:

代码语言:javascript
复制
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer

Paste_Image.png

然后打开文件目录,会发现多了一个 10w 多行的 .cpp 文件。

Paste_Image.png

通过 .cpp 文件中红我们可以看到:

  • 首先编译器生成了实例方法列 _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Student 和属性列表 _OBJC_$_PROP_LIST_Person_$_Student,两者的命名都遵循了公共前缀+类名+category名字的命名方式
  • 而且实例方法列表里面填充的正是我们在 Student 这个 category 里面写的方法 study,而属性列表里面填充的也正是我们在 Student 里添加的 age 属性。
  • 还有一个需要注意到的事实就是 category 的名字用来给各种列表以及后面的 category 结构体本身命名,而且有 static 来修饰,所以在同一个编译单元里我们的 category 名不能重复,否则会出现编译错误。
  • 其次,编译器生成了 category 本身 _OBJC_$_CATEGORY_Person_$_Student,并用前面生成的列表来初始化 category 本身。
Category的原理

想深入了解 Category 的原理,请查看苹果的源码,这里是 objc4-680/ 我们知道,Objective-C 的运行是依赖 runtime,而 runtime 则依赖于 dyld 动态加载,在 objc-os.mm 文件中可以找到入口,它的调用栈简单整理如下:

Paste_Image.png

Category 被附加到类上面是在 map_images 的时候发生的,在 new-ABI 的标准下,_objc_init 里面的调用的 map_images 最终会调用 objc-runtime-new.mm 里面的 _read_images 方法,而在 _read_images 方法的结尾,有以下的代码片段:

Paste_Image.png

Paste_Image.png

从上面的代码中可以看出:

  • Category 和它的主类(或元类)注册到哈希表中;
  • 如果主类(或元类)已实现,那么重建它的方法列表。

在这里分了两种情况进行处理:Category 中的实例方法和属性被整合到主类中;而类方法则被整合到元类中。另外,对协议的处理比较特殊,Category 中的协议被同时整合到了主类和元类中。

值得注意的是,在代码中有一小段注释 /* || cat->classProperties */,根据注释可见苹果曾经计划利用 Category 来添加属性。addUnattachedCategoryForClass 只是把类和 category 做一个关联映射,然后在 remethodizeClass 真正的去做处理。

Paste_Image.png

通过 remethodizeClass 这个函数的主要作用是将 Category 中的方法、属性和协议整合到类(主类或元类)中,查看 attachCategories 这个函数:

Paste_Image.png

首先,通过 while 循环,遍历所有的 Category,也就是参数 cats 中的 list 属性。对于每一个 Category,得到它的方法列表 mlistproplistprotolist 并存入 mlistsproplistsprotolists 中。换句话说,它的主要作用就是将 Category 中的方法、属性和协议拼接到类(主类或元类)中,更新类的数据字段 data()mlistproplistprotolist 的值。

通过以上可以看出:

  • Category 的方法没有“完全替换掉”原来类已经有的方法,也就是说如果 Category 和原来类都有 methodA,那么 category 附加完成之后,类的方法列表里会有两个 methodA
  • Category 的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的 Category 的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.09.22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Category的原理
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档