caregory (类别)
不论一个类设计的有多完美,在需求的演绎中总会碰到无法预测的情况,可以动态的为已有的类添加新行为
一、caregate主要作用
1 > 为已知类添加方法
2 > 可以把类的实现部分放在几个不同的文件里,减少单个文件的体积;可以把不同功能组织到不同的Category中;可以由多个开发者共同完成一个类
二、Category实现原理
所有OC类和对象在runtime底层都是以结构体表示的,是category_t
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;
可以看出category可以添加实例方法、类方法、实现协议、添加属性、但不能添加实例变量。这里简单说一下实例变量、属性之间的关系
@interface MyViewController :UIViewControlle
{
UIButton *yourButton;
int count;
id data;
}
@property (nonatomic, strong) UIButton *myButton;
@end
实例变量:由类定义的,除去基本数据类型 int、float,,,其他类型的变量都是实例变量
成员变量:代码中的变量、包括实例变量和基本变量类型,不需要和外界接触,默认的是protected,一般非子类都无法访问
属性:编译器自动将变量的setter和getter 方法的合成,可以用点语法来读取,可以为变量使用,可以与外界接触
实例变量 + 基本数据类型 = 成员变量
在{ }中声明的都是成员变量,所以youButton、count、data都是成员变量,实例变量是针对类而言的,成员变量用于类的内部,成员变量不会生成setter和getter方法,为了方便就有了属性,属性的好处是允许其他对象访问到该变量,因为属性的创建自动生成了setter和getter方法
如下:如果声明了实例变量就会报错
oc的运行时依赖于runtime,加载过程
拿到编译器为我们准备的category的数组,将category的实例方法、协议、属性添加到类上,把类方法协议方法加到,metaclass上,(category的各种列表添加到类上会调用 addUnattachedCategoryForClass,但他只是一个映射,真正处理事情的是re'methodizeClass,对于添加类方法会调用 attachCategoryMethods方法,他只有把所有的Category实例拼成一个大的实例方法交给attachMethodList方法),然而Category的方法并没有替换原来的方法,会覆盖原来的方法,因为Category的方法被放到了新列表的前面,在运行时查找方法的时候是顺着方法列表的顺序查找的,只要查找到了对应的名字方法,就会调用
为什么Category中 不能添加实例变量?
1、前边说过,Category本身的定义中没有实例变量的数组,但在runtime中确实有一个函数:class_addIvar().用来给类添加实例变量,但是这个函数只能在构建一个雷的过程中调用,一旦类完成了定义,就不能再添加成员变量了,类在经过编译期之后就启动了runtime,就没有机会调用addIvar方法了,程序在运行时动态的构建类会调用objc_allocateClasspair之后,但在objc_registerClass之前才可以被调用,同样是没有机会。
2、一块区域内存包含了所有的成员变量和isa指针,因此在编译的时候就已经分配好了内存,所以动态的改变了成员变量就改变了这块内存的布局
那么如果一定要才Category中实现添加变量
OC是一门动态的语言,runtime中有两个函数objc_getAssoctatedObject / objc_setAssoctatdObject 来访问和生成关联对象,这两个方法可以让一个对象和另一个对象关联,也就是说一个对象保持对另一个对象的引用,并获取那个对象
定义一个属性,重写setter和getter方法
在setter和getter方法中返回 return objc_getAssoctatedObject(self,name) 和 objc_setAssoctatedObject(self,nameKey,name,now)
extension 和 Category 比较
extension看起来像一个匿名的Category,但是extension和有名字的Category是两个不同的东西,extension在编译期决定,他就是类的一部分,在编译期将头文件中的interface和实现里边的implement一起形成一个完整的类,一般用来隐藏隐藏类的私有信息,必须要有一个类的源码才能添加,它伴随着这个类的产生而产生,消失而消失,因此他可以添加实例变量、属性、方法,但都是私有方法。
Category是在运行期决定的,他是无法添加实例变量的,在编译期对象的内存分布已经确定,如果添加实例变量会破坏类的内存布局。