一,首先为啥要使用分类?
- 开发中分类可以吧不同的功能分散到多个不同的文件及框架,减少单个文件的体积,方便管理
- 创建私有方法
二,Category在编译时刻,都是独立的,各自生成各自的文件
XZPerson
#import <Foundation/Foundation.h>
@interface XZPerson : NSObject
@end
#import "XZPerson.h"
@implementation XZPerson
@end
XZPerson的分类
#import "XZPerson.h"
@interface XZPerson (Eat)
- (void)eat;
@end
#import "XZPerson+Eat.h"
@implementation XZPerson (Eat)
- (void)eat{
NSLog(@"eat");
}
+ (void)run{
NSLog(@"run");
}
@end
使用xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 查看分类(分析的是编译时刻生成的cpp文件)
struct _category_t {
const char *name;
struct _class_t *cls;
//对象方法
const struct _method_list_t *instance_methods;
//类方法
const struct _method_list_t *class_methods;
//协议
const struct _protocol_list_t *protocols;
//属性列表
const struct _prop_list_t *properties;
};
static struct _category_t _OBJC_$_CATEGORY_XZPerson_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"XZPerson",
0, // &OBJC_CLASS_$_XZPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_XZPerson_$_Eat,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_XZPerson_$_Eat,
0,
0,
};
其中 _OBJC_$_CATEGORY_INSTANCE_METHODS_XZPerson_$_Eat对应的
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_XZPerson_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"eat", "v16@0:8", (void *)_I_XZPerson_Eat_eat}}
};里面存放了对象方法
其中_OBJC_$_CATEGORY_CLASS_METHODS_XZPerson_$_Eat 对应的
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_XZPerson_$_Eat __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"run", "v16@0:8", (void *)_C_XZPerson_Eat_run}}
};里面存放了类方法
综上可知分类的对象方法最终也会到类对象的对象方法列表中,类方法也会到元类的类方法列表中。
下面我们来分析下是如何做到的?
1.这个要去研究下runtime的源码,通过Runtime加载某个类的所有Category数据
源码解读顺序objc-os.mm~_objc_init~map_images(镜像)~map_images_nolock~objc-runtime-new.mm~_read_images
~remethodizeClass(重新方法化)~attachCategories(方法附加)~attachLists~realloc、memmove、 memcpy
memcpy是从内存左侧一个字节一个字节的复制拷贝
memmove是将需要拷贝的内容一次性移动拷贝。
2. 把所有Category的方法、属性、协议数据,合并到一个大数组中
后面参与编译的Category数据,会在数组的前面
3.将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面
所以:分类+类中如果含有相同的方法,会优先调用分类中的方法,假如两个分类(针对同一个类的不同分类)含有相同的方法,调用顺序跟编译的顺序有关,根据前面的结论会直接优先调用最后参与编译的分类。