目录
- category
1. category
category简介
category是Objective-C 2.0之后添加的语言特性,category的主要作用是为已经存在的类添加方法。除此之外,apple还推荐了category的另外两个使用场景
- 可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处,a)可以减少单个文件的体积 b)可以把不同的功能组织到不同的category里 c)可以由* 多个开发者共同完成一个类 d)可以按需加载想要的category 等等。
- 声明私有方法
category和extension
- extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。
- 但是category则完全不一样,它是在运行期决议的。
就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
category真面目
我们知道,所有的OC类和对象,在runtime层都是用struct表示的,category也不例外,在runtime层,category用结构体category_t(在objc-runtime-new.h中可以找到此定义)
我们使用clang的命令去看看category到底会变成什么:
clang -rewrite-objc MyClass.m
typedef 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;
} category_t;
在runtime层,category用结构体category_t(在objc-runtime-new.h中可以找到此定义),它包含了
- 类的名字(name)
- 类(cls)
- category中所有给类添加的实例方法的列表(instanceMethods)
- category中所有添加的类方法的列表(classMethods)
- category实现的所有协议的列表(protocols)
- category中添加的所有属性(instanceProperties)
category的各种列表是怎么最终添加到类上的,就拿实例方法列表来说吧:
- 把category的实例方法、协议以及属性添加到类上
- 把category的类方法和协议添加到类的metaclass上
把类和category做一个关联映射,把所有category的实例方法列表拼成了一个大的实例方法列表,然后转交给了attachMethodLists方法。
需要注意的有两点:
- category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA
- category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休_,殊不知后面可能还有一样名字的方法。
category和关联对象
我们知道在category里面是无法为category添加实例变量的。但是我们很多时候需要在category中添加和对象关联的值,这个时候可以求助关联对象来实现。
但是关联对象又是存在什么地方呢? 如何存储? 对象销毁时候如何处理关联对象呢?
我们可以看到所有的关联对象都由AssociationsManager管理。AssociationsManager里面是由一个静态AssociationsHashMap(哈希表)来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map(表)的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map(表)的value又是另外一个AssociationsHashMap,里面保存了关联对象的键值对。可以简单理解成字典一样存取。
而在对象的销毁逻辑里面:runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。
这里涉及到了3个函数:
这里涉及到了3个函数:
//setter,就像字典中的 setValue:ForKey:
void objc_setAssociatedOject(id object, void *key, id value, objc_AssociationPolicy policy)
//getter,就像字典中的 objectForKey
id objc_getAssociatedObject(id object, void *key)
//remove,就像字典中的 removeAllObject
void objc_removeAssocaitedObjected(id object)
网友评论