本文主要介绍分类如何加载到类中,还有分类和类搭配使用的情况
分类本质
🌰main函数定义SLPerson分类
@interface SLPerson (SL)
@property(nonatomic, copy)NSString * cate_name;
@property(nonatomic, assign)int cate_age;
-(void)cate_instanceMethod1;
-(void)cate_instanceMethod2;
-(void)cate_instanceMethod3;
+(void)cate_sayClassMethod;
@end
@implementation SLPerson (SL)
-(void)cate_instanceMethod1 {
NSLog(@"%s", __func__);
}
-(void)cate_instanceMethod2 {
NSLog(@"%s", __func__);
}
-(void)cate_instanceMethod3 {
NSLog(@"%s", __func__);
}
+(void)cate_sayClassMethod {
NSLog(@"%s", __func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
SLPerson * p = [[SLPerson alloc]init];
[p cate_instanceMethod1];
// [SLPerson say2];
}
return 0;
}
clang编译,查看cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_SLPerson;
static struct _category_t _OBJC_$_CATEGORY_SLPerson_$_SL __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"SLPerson",
0, // &OBJC_CLASS_$_SLPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_SLPerson_$_SL,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_SLPerson_$_SL,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_SLPerson_$_SL,
};
static void OBJC_CATEGORY_SETUP_$_SLPerson_$_SL(void ) {
_OBJC_$_CATEGORY_SLPerson_$_SL.cls = &OBJC_CLASS_$_SLPerson;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
(void *)&OBJC_CATEGORY_SETUP_$_SLPerson_$_SL,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_SLPerson_$_SL,
};
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
分类的本质是struct _category_t,其定义如下
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
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;
};
实例方法instance method底层实现如下,有三个方法(SEL + 签名 + 函数地址)
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_SLPerson_$_SL __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"cate_instanceMethod1", "v16@0:8", (void *)_I_SLPerson_SL_cate_instanceMethod1},
{(struct objc_selector *)"cate_instanceMethod2", "v16@0:8", (void *)_I_SLPerson_SL_cate_instanceMethod2},
{(struct objc_selector *)"cate_instanceMethod3", "v16@0:8", (void *)_I_SLPerson_SL_cate_instanceMethod3}}
};
搜_objc_method,
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type; //方法签名
void *_imp; //函数地址
};
分类中定义了属性,但是底层编译时并没有,主要是因为定义的属性没有对应的set和get方法,可通过关联对象来设置
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[2];
} _OBJC_$_PROP_LIST_SLPerson_$_SL __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
2,
{{"cate_name","T@\"NSString\",C,N"},
{"cate_age","Ti,N"}}
};
分类的加载
分类加载的顺序和Build Phases 中Compile Sources的顺序是一样的,拖拽文件即可改变顺序 。在底层实现attachCategories中涉及rwe的加载,分类的加载顺序是,越晚加进来,越在前面。分类的加载步骤如下:
- 根据类和分类是否实现load来区分不同时机
- attachCategories:准备分类数据
- attachLists:将分类数据添加到主类中
分类和类的搭配使用
非懒加载类 + 非懒加载分类(主类实现了+load方法,分类也实现了+load方法
):数据加载在load_images
方法中,先对类进行加载,然后把分类的信息贴到类中
非懒加载类 + 懒加载分类(主类实现了+load方法,分类未实现+load方法
):在read_image
就加载数据,数据来自data
,data在编译时期
就已经完成,即data中除了类的数据,还有分类的数据,与类绑定在一起
懒加载类 + 懒加载分类
(主类和分类均未实现+load方法):数据加载推迟到第一次消息
时,数据同样来自data
,data在编译时期
就已经完成
懒加载类 + 非懒加载分类(
主类未实现+load方法, 分类实现了+load方法)
,只要分类实现了load,会迫使主类提前加载
,即在_read_images
中不会对类做实现操作,需要在load_images
方法中触发类的数据加载,即rwe初始化
,同时加载分类数据
如下图,(图片转载自Style_月月,感谢🙏)