category原码
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;
};
通过查看源码可以看出,给分类可以添加实例方法,类方法,协议,属性(无法添加实例变量)。
添加分类NSPerson+Add类
#import "NSPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface NSPerson (Add)
@property (nonatomic,strong)NSString *testStr;
- (void)add;
+ (void)testAdd;
@end
#import "NSPerson+Add.h"
@implementation NSPerson (Add)
- (void)add{
}
+ (void)testAdd{
}
@end
使用clang查看编译源码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc NSPerson+Add.m
//NSPerson 结构体
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;
};
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
#pragma warning(disable:4273)
//分类添加的实例方法列表
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_NSPeson_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"add", "v16@0:8", (void *)_I_NSPeson_Add_add}}
};
//分类添加的类方法列表
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_NSPeson_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
//方法名字
{{(struct objc_selector *)"testAdd", "v16@0:8", (void *)_C_NSPeson_Add_testAdd}}
};
//添加的属性列表
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_NSPeson_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
//testStr属性名字
{{"testStr","T@\"NSString\",&,N"}}
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSPeson;
// *********NSPerson+Add的结构体*************
static struct _category_t _OBJC_$_CATEGORY_NSPeson_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSPeson",//name
0, // &OBJC_CLASS_$_NSPeson,
//实例方法列表
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSPeson_$_Add,
//类方法列表
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_NSPeson_$_Add,
0,
//属性列表
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_NSPeson_$_Add,
};
static void OBJC_CATEGORY_SETUP_$_NSPeson_$_Add(void ) {
_OBJC_$_CATEGORY_NSPeson_$_Add.cls = &OBJC_CLASS_$_NSPeson;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
(void *)&OBJC_CATEGORY_SETUP_$_NSPeson_$_Add,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_NSPeson_$_Add,
};
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
通过编译器生成的实例方法列表,类方法列表和属性列表都对应的赋值给添加的分类的结构体中,可以看出在编译时期,分类添加的方法和属性是放到分类本身的结构体中
// *********NSPerson+Add的结构体*************
static struct _category_t _OBJC_$_CATEGORY_NSPeson_$_Add __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSPeson",//name
0, // &OBJC_CLASS_$_NSPeson,
//实例方法列表
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSPeson_$_Add,
//类方法列表
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_NSPeson_$_Add,
0,
//属性列表
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_NSPeson_$_Add,
};
为什么分类的方法会覆盖类的方法?
在runtime运行时,会先遍历找出所有的分类,然后在将原来的类的方法添加新的方法列表中,然后根据分类的数据扩容,在将分类的方法添加到类的类对象或元类的方法中时,使用将原来类的方法向后移,这样的话分类的方法在原来类的方法的前面,所以会覆盖到原来的类方法,(注并不真正的覆盖,而且分类的方法调用顺序靠前,在找到方法后,就不会在继续往下找)
后编译的分类,先调用
Category和Class Extension的区别是什么?
Class Extension在编译的时候,它的数据就已经包含在类信息中
Category是在运行时,才会将数据合并到类信息中
网友评论