Category
1. 初次见面
Category
是Objective-C 2.0之后添加的语言特性,Category
的主要作用是为已经存在的类添加方法
。除此之外,Apple还推荐了Category
的另外两个使用场景(详见 1):
- 可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处:
可以减少单个文件的体积
可以把不同的功能组织到不同的Category里
可以由多个开发者共同完成一个类
可以按需加载想要的Category 等等
- 声明私有方法
不过除了apple推荐的使用场景,广大开发者脑洞大开,还衍生出了Category的其他几个使用场景:
- 模拟多继承
- 把framework的私有方法公开
Objective-C的这个语言特性对于纯动态语言来说可能不算什么,比如javascript,你可以随时为一个“类”或者对象添加任意方法和实例变量。但是对于不是那么“动态”的语言而言,这确实是一个了不起的特性。
2. 与Extension的区别
Extension
看起来很像一个匿名的Category
,但是Extension
和有名字的Category
几乎完全是两个东西。
Extension
在编译期决议,它就是类的一部分。在编译期和头文件里的@interface
以及实现文件里的@implement
一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。
Extension
一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加Extension
,所以你无法为系统的类比如NSString添加Extension
。(详见 2)
但是Category
则完全不一样,它是在运行期决议的。
就Category
和Extension
的区别来看,我们可以推导出一个明显的事实,Extension
可以添加实例变量,而Category
是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。
3. 揭开面纱
我们知道,所有的OC类和对象,在runtime
层都是用struct表示的,Category
也不例外,在runtime层,Category
用结构体category_t(在objc-runtime-new.h中可以找到此定义),它包含了:
- 类的名字(name)
- 类(class)
- Category中所有给类添加的实例方法的列表(instanceMethods)
- Category中所有添加的类方法的列表(classMethods)
- Category实现的所有协议的列表(protocols)
- Category中添加的所有属性(instanceProperties)