重学OC第十三篇:类的加载(下)

前言

在这里插入图片描述
为了了解分类,写了下面的TestA类和它的三个分类,上图是三个分类文件的编译顺序。 TestA类中有一个testA的实例方法和一个load类方法,TestA (One)和TestA (Two)中同样实现了这两个方法,TestA (Three)中只实现了load方法。TestA(One)中还声明了一个属性oneStr。

@interface TestA : NSObject 
- (void)testA;
@end

@implementation TestA

+ (void)load {
   
    NSLog(@"TestA load");
}

- (void)testA {
   
    NSLog(@"TestA method name : %s", __func__);
}
@end
@interface TestA (One)

@property (nonatomic, copy) NSString *oneStr;
- (void)testA;

@end

@implementation TestA (One) 

+ (void)load {
   
    NSLog(@"TestA+One load");
}

- (void)testA {
   
    NSLog(@"TestA+One method name : %s", __func__);
}
@end
@interface TestA (Two)

- (void)testA;

@end

@implementation TestA (Two)

+ (void)load {
   
    NSLog(@"TestA+Two load");
}

- (void)testA {
   
    NSLog(@"TestA+Two method name : %s", __func__);
}

@end

@interface TestA (Three)

@end

@implementation TestA (Three)

+ (void)load {
   
    NSLog(@"TestA+Three load");
}

@end


int main(int argc, const char * argv[]) {
   
    @autoreleasepool {
   
        TestA *testA = [[TestA alloc] init];
        [testA testA];
    }
    return 0;
}

一、分类的本质

通过clang命令把分类的TestA+One.m转为c++的.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 /*_prop_list_t*/ {
   
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_TestA_$_One __attribute__ ((used, section ("__DATA,__objc_const"))) = {
   
	sizeof(_prop_t),
	1,
	{
   {
   "oneStr","T@\"NSString\",C,N"}}
};

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_TestA_$_One __attribute__ ((used, section ("__DATA,__objc_const"))) = {
   
	sizeof(_objc_method),
	1,
	{
   {
   (struct objc_selector *)"testA", "v16@0:8", (void *)_I_TestA_One_testA}}
};

可从中得出_category_t的结构体,对应于objc源码为

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;   //实现属性
    struct property_list_t *_classProperties;  //类属性
}

从.cpp中也可以看到分类One中的属性oneStr虽在属性列表_OBJC_KaTeX parse error: Expected group after '_' at position 17: …PROP_LIST_TestA_̲One中存在,但是分类实例方法列表_OBJCKaTeX parse error: Expected group after '_' at position 33: …E_METHODS_TestA_̲_One中并没有oneStr/setOneStr方法。可知分类的属性只是声明,需要自己去实现setter/getter。Xcode中也会有警告提示。

二、分类加载

2.1 线索一:从map_images引出load_images中的loadAllCategories,再到load_categories_nolock中的attachCategories

在前面文章分析map_images方法源码时,在懒加载代码前有一段关于分类的代码

//仅在完成初始分类附件后才执行此操作。对于启动时出现的类别,将发现推迟到对_dyld_objc_notify_register的调用完成后的第一个load_images调用之后。
if (didInitialAttachCategories) {
   
	for (EACH_HEADER) {
   
        load_categories_nolock(hi);
    }
}

从注释中的信息找到load_images中以下代码

//didInitialAttachCategories初始为flase,didCallDyldNotifyRegister在objc_init最后初设为了true
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
   
   didInitialAttachCategories = true;
   loadAllCategories();
}
static void loadAllCategories() {
   
    mutex_locker_t lock(runtimeLock);
    for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
   
        load_categories_nolock(hi);
    }
}

static void load_categories_nolock(header_info *hi) {
   
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;
    auto processCatlist = [&](category_t * const *catlist) {
   
        for (unsigned i = 0; i < count; i++) {
   
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{
   cat, hi}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值