笔记-如何优雅姿势探究类结构(类的底层原理解析)
2019-04-14 本文已影响0人
佐_笾
类的底层原理
实例对象、类对象、元类之间的关系
直接上代码,看结果之后解释一下
image
思考一下这几个问题:类对象
class1
、class2
、class3
打印的地址分别是什么情况?
为什么class4
是元类,class5
是根元类?
打印结果
image
可以看出:
类对象class1
、class2
、class3
的地址是同一个,因为一个对象的类对象只有一个。
object_getClass
获取对象的类,类对象存储的位置是哪里?在文章笔记-runtime源码解析之让你彻底了解底层源码里讲述过,它是存在元类中,所以class4为元类,同样class5为根元类。如果还有疑问的话,可以接着往后看,或者评论里留言给笔者。
下面进行一些lldb调试,直接上图
- 用
person->isa
输出了person
对象的isa
的指向,是ZBPerson
这个类,地址是0x6000034232c0
,调试的输出的结果和打印输出的结果一致 - 用命令
x 0x6000034232c0
输出的是ZBPerson
这个类内存的情况,图上说明了,前八字节指的是isa
,为什么呢?看下面几段源码
Class
是objc_class
类型的image image
上面一段源码里可以看出,类的内部结构,前8字节为隐藏属性
isa
,接着后8字节是superclass
,接着是16字节的
cache
等等,具体的后面分析。
- 上面lldb调试过程中也说类,
isa
为优化过的,每次打印输出的时候,都&上了一个值0x00007ffffffffff8
,这又是从哪里得出来的结论呢?请看下面源码:
image
嗯,讲述到这里,上面的lldb调试的过程,相信你是可以明白的,其实最终还是回到文章笔记-runtime源码解析之让你彻底了解底层源码里的一幅图
image
类结构
直接上源码
上面源码,我只截图了部分代码,下面其实还有很长,这里不做说明。
isa、superclass、cache
在上面简单描述了,这里不再重复,着重看下面,直接看下面代码image
这里有我们熟悉的
methods、properties、protocols
,往下走
image
image
这里的list_array_tt
是一个二级指针型的,存放着我们常用的属性列表、方法列表、协议列表。
上面的截图,我也只截图了一部分,里面有个标红的protected
,其实还有着private
、public
。
这又与我们日常的开发的公有属性、私有属性等等相呼应。
除了methods、properties、protocols
,还有一个class_ro_t
需要看一下,上源码
这里有到面试题,看下面代码:
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_t
为const
。一旦这个类创建完毕,就不能进行修改.
MachOView查看类结构
先编译运行项目,然后找到可执行文件
imageimage
image
把这个可执行文件拖到MachOView里,下图显示
image
然后打印出当前类的地址,通过image list
找到首地址,通过计算器算出偏移量
得到结果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
细看一下
输出的结果很明确里,当打印
baseMethodList
时,还同时给出里方法名、方法签名、所在的类以及多少行;有兴趣的读者还可以通过这种方式打印出类里的其他内容。
这一切看上去似乎很完美,给大家看一下ZBPerson.m
文件里的内容
那么问题就来了,上面的
lldb
打印只打印出了instanceMethod
方法,那其他两个方法都去哪里了呢?
nice~类方法存储在元类中,上面调试的都是类对象的结构,下面的就是类方法的调试
image