作用
可以在不修改原来类的基础上,为一个类扩展方法。
分类的结构体
struct _category_t {
const char *name;//类名
struct _class_t *cls;//类
const struct _method_list_t *instance_methods;//category中所有给类添加的实例方法的列表(instanceMethods)
const struct _method_list_t *class_methods;//category中所有添加的类方法的列表(classMethods)
const struct _protocol_list_t *protocols;//category实现的所有协议的列表(protocols)
const struct _prop_list_t *properties;//category中添加的所有属性(instanceProperties)
};
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; // 属性列表
// 如果是元类,就返回类方法列表;否则返回实例方法列表
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) {
return classMethods;
} else {
return instanceMethods;
}
}
// 如果是元类,就返回 nil,因为元类没有属性;否则返回实例属性列表,但是...实例属性
property_list_t *propertiesForMeta(bool isMeta) {
if (isMeta) {
return nil; // classProperties;
} else {
return instanceProperties;
}
}
};
编译时的分类
分类的属性
// Person(categoryPerson) 属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"categoryPersonName","T@\"NSString\",C,N"}}
};
// Person 属性列表
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"presonName","T@\"NSString\",C,N,V_presonName"}}
};
在分类中可以声明属性,并且同样会生成一个 _prop_list_t 的结构体
分类的实例变量?
// Person 实例变量列表
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
1,
{{(unsigned long int *)&OBJC_IVAR_$_Person$_presonName, "_presonName", "@\"NSString\"", 3, 8}}
};
因为 _category_t 这个结构体中并没有 _ivar_list_t
所以在编译时系统没有Person(categoryPerson) 没有生成类似的相应结构体,也没有生成 _categoryPersonName。
分类的实例方法
// Person 实例方法结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"doSomeThing", "v16@0:8", (void *)_I_Person_doSomeThing},
{(struct objc_selector *)"presonName", "@16@0:8", (void *)_I_Person_presonName},
{(struct objc_selector *)"setPresonName:", "v24@0:8@16", (void *)_I_Person_setPresonName_}}
};
// Person(categoryPerson )实例方法结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"doSomeThing", "v16@0:8", (void *)_I_Person_categoryPerson_doSomeThing}}
};
虽然分类可以声明属性,但是编译时,系统并没有生成分类属性的 get/set 方法,所以,这就是为什么分类要利用
分类的结构体
// Person(categoryPerson ) 结构体
static struct _category_t _OBJC_$_CATEGORY_Person_$_categoryPerson __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_categoryPerson,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_categoryPerson,
};
这是系统在编译时实例化 _category_t 生成的
OBJC$CATEGORY_Person$_categoryPerson
分类数组
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Person_$_categoryPerson,
};
编译器最后生成了一个数组,数组的元素就是我们创建的各个分类,用来在运行时加载分类。
分类的加载
加载分类调用栈
_objc_init
└──map_2_images
└──map_images_nolock
└──_read_images
分类加载的调用栈如上述
_objc_init 算是整个 objc4 的入口,进行了一些初始化操作,注册了镜像状态改变时的回调函数
map_2_images 主要是加锁并调用 map_images_nolock
map_images_nolock 在这个函数中,完成所有 class 的注册、fixup等工作,还有初始化自动释放池、初始化 side table 等工作并在函数后端调用了 _read_images
_read_images 方法干了很多苦力活,比如加载类、Protocol、Category,加载分类的代码就写在 _read_images 函数的尾部
该调用栈入口函数 void _objc_init(void) 在 objc-os.mm 中