类的加载(2)

 本文主要介绍分类如何加载到中,还有分类和类搭配使用的情况

分类本质

🌰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 PhasesCompile 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_月月,感谢🙏)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值