笔记-如何优雅姿势探究类结构(类的底层原理解析)

2019-04-14  本文已影响0人  佐_笾

类的底层原理

实例对象、类对象、元类之间的关系

直接上代码,看结果之后解释一下


image

思考一下这几个问题:类对象class1class2class3打印的地址分别是什么情况?
为什么class4是元类,class5是根元类?

打印结果


image

可以看出:
类对象class1class2class3的地址是同一个,因为一个对象的类对象只有一个。
object_getClass获取对象的类,类对象存储的位置是哪里?在文章笔记-runtime源码解析之让你彻底了解底层源码里讲述过,它是存在元类中,所以class4为元类,同样class5为根元类。如果还有疑问的话,可以接着往后看,或者评论里留言给笔者。
下面进行一些lldb调试,直接上图

image image
Classobjc_class类型的
image image
上面一段源码里可以看出,类的内部结构,前8字节为隐藏属性isa,接着后8字节是superclass
接着是16字节的cache等等,具体的后面分析。 image
image
嗯,讲述到这里,上面的lldb调试的过程,相信你是可以明白的,其实最终还是回到文章笔记-runtime源码解析之让你彻底了解底层源码里的一幅图
image

类结构

直接上源码

image
上面源码,我只截图了部分代码,下面其实还有很长,这里不做说明。
isa、superclass、cache在上面简单描述了,这里不再重复,着重看下面,直接看下面代码
image
这里有我们熟悉的methods、properties、protocols,往下走 image
image

这里的list_array_tt是一个二级指针型的,存放着我们常用的属性列表、方法列表、协议列表。
上面的截图,我也只截图了一部分,里面有个标红的protected,其实还有着privatepublic
这又与我们日常的开发的公有属性、私有属性等等相呼应。

除了methods、properties、protocols,还有一个class_ro_t需要看一下,上源码

image

这里有到面试题,看下面代码:

    Class ZBClass = objc_allocateClassPair([NSObject class], "ZBClass", 0);
    // 添加方法--属性
    NSString *name = @"name";
    
    objc_registerClassPair(ZBClass);
    class_addIvar(ZBClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
    
    id zbClass = [ZBClass alloc];
    [zbClass setValue:@"Zuobian" forKey:@"name"];
    NSLog(@"name == %@", [zbClass valueForKey:@"name"]);

上面这段代码,能否正常运行?

image
报错指出没有这个key,但是上面代码中确实已经添加,那么只是说明添加失败了,为什么呢?

如果把这两句话颠倒一下,打印查看结果

    class_addIvar(ZBClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
    objc_registerClassPair(ZBClass);
image
说明在注册这个函数之前添加Ivar是成功的,回到class_ro_t源码可以看出
image
ivar_list_tconst。一旦这个类创建完毕,就不能进行修改.

MachOView查看类结构

先编译运行项目,然后找到可执行文件

image
image
image
把这个可执行文件拖到MachOView里,下图显示
image

然后打印出当前类的地址,通过image list找到首地址,通过计算器算出偏移量

image
得到结果0x3FE0,然后到MachOView里查找
image

通过lldb调试查看类结构

编译运行下面代码

image

得到里类对象以及元类对象的地址。

image
通过源码可以知道,我们想要得到class_rw_t *data(),就需要知道class_data_bits_t bits,而要知道class_data_bits_t bits,我们就需要通过类对象的地址进行指针偏移来获得。8字节+8字节+16字节--->移2位便能获取到class_data_bits_t bits
image
找到class_rw_t *data()后,打印出来
image
对比下面的源码看一下
image
对比之后,是不是我们想要的东西都在里面class_ro_t、methods、properties、protocols

再进入class_ro_t细看一下

image
输出的结果很明确里,当打印baseMethodList时,还同时给出里方法名、方法签名、所在的类以及多少行;有兴趣的读者还可以通过这种方式打印出类里的其他内容。

这一切看上去似乎很完美,给大家看一下ZBPerson.m文件里的内容

image
那么问题就来了,上面的lldb打印只打印出了instanceMethod方法,那其他两个方法都去哪里了呢?

nice~类方法存储在元类中,上面调试的都是类对象的结构,下面的就是类方法的调试


image
上一篇下一篇

猜你喜欢

热点阅读