iOS 底层原理之 类的原理分析(下)

在上一篇章类的原理分析(上)我们大致分析了类的基本结构,以及isa的走位图,本篇将延续上篇进行细致分析

准备工作:WWDC 2020 - 类优化相关视频LLVM源码

一.类优化

通过上面的视频内容,我们大致做一下总结

类的内存图解

在这里插入图片描述

1.Clean Memory

是指加载后不会发生更改的内存,class_ro_t是属于Clean Memory的。

class_ro_t(只读):一个指向更多数据的指针,存储额外信息的结构class_ro_tro代表只读,存放了方法、协议、实例变量的信息

2.Dirty Memory

是指在进程运行时会发生更改的内存,类的结构一经使用就会变成Dirty Memory,因为运行时会向它写入数据,这里指的是class_rw_t

Dirty Memory 是这个类被分成两部分的原因,可以保持类加载后不会发生更改的数据越多越好,通过分离永远不会更改的数据,可以把大量的类数据存储为Clean Memory

class_rw_t(读写): Methods、Properties、Protocols,当category被加载时,它可以向类中添加新方法,可以根据Method Swizzling方式修改,因为class_ro_t是只读,所以要把这些放在class_rw_t中。

这是在视频中截取的示意图

在这里插入图片描述

3.class_rw_t优化

通过上面的描述可以看出,class_rw_t是在类被使用的时候,运行时分配的内存,但是我们又发现其实和class_ro_t存储的信息是一样的,只不过一个只读,另一个读写,那么问题就来了

问题1:为什么方法,属性在class_ro_t中时,class_rw_t还要有方法,属性呢?

因为它们可以在运行时进行更改,当category被加载时,它可以向类中动态添加新的方法,通过Runtime API手动向类中添加属性和方法,而class_ro_t 是只读的,所以我们需要在class_rw_t中来跟踪这些东西。

问题2class_rw_t结构在苹果手机中,占用很多的内存,那么如何去缩小这些结构呢?

视频中说到:我们在读取—编写部分需要这些东西,因为它们在运行时可以被修改,但是大约10%的类是需要修改它们的方法,而且只有在swift中才会使用这个demangledName字段,但是swift类并不需要这个字段,除非是访问它们Objective-C名称时才需要。

优化:因此就有了优化的手段,也就是剥离平时用不到的部分,把class_rw_t用到的部分拆分出来,在运行时,分配内存,所以就有了class_rw_ext_t

在这里插入图片描述

二.梳理总结

其实在类的原理分析(上)中我们基本上已经把类的基本结构都通过lldb的方式,摸得七七八八了,包括在重识alloc流程(下)中我们也通过clang编译的方式,也看过我们上层代码在底层的实现,包括一些属性的声明,成员变量等等,再结合今天前面给的官方提供的视频解释,基本就可以得出结论

  • 成员变量是声明在类的{}中的

  • 属性是用@property方式声明的,会自动生成getter和setter方法,并且在底层编译阶段会变成_方式的成员变量

  • 以对象类型声明的(特殊的成员变量)

  • 如果声明的对象不设置如何除atomic之外的属性,那么默认属性是strong,不会触发objc_setProperty,也就是内存偏移实现

  • copy修饰的属性使用objc_setProperty方式实现,其它属性使用内存偏移实现

  • 苹果没有把所有的setter方法全部写在底层,因为如果底层需要维护,修改起来特别麻烦。搞了个适配器中间层,中间层的作用是供上层的setter调用,中间层对属性的修饰符进行判断走不同的流程,调用底层的方法实现。中间层的优点:底层变化上层不受影响,上层变化底层也不会受影响

我们在底层代码中可以看到_method_list_t中那些生成的方法,都有一串特殊的字符,如下


static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[24];
} _OBJC_$_INSTANCE_METHODS_LGPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	24,
	{{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
	{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
	{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
	{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_},
	{(struct objc_selector *)"nnickName", "@16@0:8", (void *)_I_LGPerson_nnickName},
	{(struct objc_selector *)"setNnickName:", "v24@0:8@16", (void *)_I_LGPerson_setNnickName_},
	{(struct objc_selector *)"anickName", "@16@0:8", (void *)_I_LGPerson_anickName},
	{(struct objc_selector *)"setAnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAnickName_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
	{(struct objc_selector *)"aname", "@16@0:8", (void *)_I_LGPerson_aname},
	{(struct objc_selector *)"setAname:", "v24@0:8@16", (void *)_I_LGPerson_setAname_},
	{(struct objc_selector *)"nickName", "@16@0:8", (void *)_I_LGPerson_nickName},
	{(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},
	{(struct objc_selector *)"acnickName", "@16@0:8", (void *)_I_LGPerson_acnickName},
	{(struct objc_selector *)"setAcnickName:", "v24@0:8@16", (void *)_I_LGPerson_setAcnickName_};
	

这些其实是函数的一种特殊表现形式,官方也给出了字符对应的信息Apple Documents地址,大家可自行查阅

此图是从其他大佬的文章中借鉴而来,本人比较懒就没有去画图了,哈哈,图源在此
在这里插入图片描述

三.探索isKindOfClass和isMemberOfClass

1.测试题

#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import "LGTeacher.h"

void lgKindofDemo(void){
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];     //
    BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
    BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];     //
    NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);

    BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
    BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];     //
    BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //
    BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];     //
    NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        lgKindofDemo();

    }
    return 0;
}

2.结果

2021-06-22 15:31:32.186957+0800 KCObjcBuild[21446:304632]  re1 :1
 re2 :0
 re3 :0
 re4 :0
2021-06-22 15:31:32.189482+0800 KCObjcBuild[21446:304632]  re5 :1
 re6 :1
 re7 :1
 re8 :1
Program ended with exit code: 0

3.分析

通过源码分析得出,当然其实参照上篇的isa走位图也能分析出来

//题1:[(id)[NSObject class] isKindOfClass:[NSObject class]];   1
//cls =NSObject tcls = self->ISA() =  NSObject  tcls->getSuperclass() = nil
//NSObject 属于根类 根类的元类是NSObject根元类 根元类父类所以是自己NSObject

//题3:[(id)[LGPerson class] isKindOfClass:[LGPerson class]];   0
//cls =LGPerson tcls = self->ISA() = (LGPerson元类)  tcls->getSuperclass()=(LGPerson元类的父类)=父类的元类 =(NSObject元类)
//LGPerson 元类存在 暂时称呼为(LGPerson元类)  而元类的父类自然而然也存在应该就是NSObject(NSObject是LGPerson父类)

//通过代码分析类方法 isKindOfClass:本质就是当前类和当前类的元类对比,相同则是yes,不相同则从元类的父类继续找,不断寻找父类的元类进行对比,直到没有则为NO
//cls和当前类的元类开始对比,不断元类父类递进
+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

//题5:[(id)[NSObject alloc] isKindOfClass:[NSObject class]];  1
//tcls = [self class] NSObject类 cls也是NSObject类

//题6:[(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];  1
// tcls = [self class] LGPerson类 cls也是LGPerson类
//对象方法isKindOfClass: 本质是当前类和对象的类开始对比,相同则是yes,不相同则从对象的类的父类继续找,不断寻找父类的元类进行对比,直到没有则为NO
//cls和当前类开始对比,不断父类递进
- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

//cls和当前类的元类对比
//BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  0
//BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];  0
+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

//[(id)[NSObject alloc] isMemberOfClass:[NSObject class]];  1
//[(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];  1
//cls和当前对象的类对比
- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

PS:有不少地方没有描述清楚,后续会详细补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值