一直都说类最终都会编译为struct
,可是怎么验证呢?编译后的结构体内部都会有些什么东西呢?
//Clang默认依赖Foundation库
//当前目录下:把目标文件编译成c++文件.pp
clang -rewrite-objc main.m -o main.cpp
//编译目标文件内有UIKit等其他库需要导入依赖,
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / Applications/Xcode.app/Contents/Developer/Platforms/ iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
//使用`xcode`安装安装的`xcrun`命令
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp (真机)
运行命令后,打开main.cpp。好长好长,但是无所谓我们关注的是:
struct
进一步验证了类是一个结构体。(之struct NSObject_IMPL NSObject_IVARS前,文章中提到类是继承结构体objc_object万物之源)struct NSObject_IMPL NSObject_IVARS
,成员变量:HRTestName
isa
没跑了。get
,set
方法的形式存在。因为在isa使用了一种位域
技术,来保存内部信息,这里简单介绍一下联合体、位域
联合体(union):各变量是“互斥”的,同时只能有一个变量有值,且公用同一块内存。优点是内存使用更为精细灵活,也节省了内存空间。C语言共用体详解
如果有一个需求,需要能表达东南西北
四个方向。第一想到的就是创建4个Bool值来进行控制,可是4个Bool需要:4个字节
。
现在需求变了,需要还能表达东南
,西南
,西北
,东北
,继续创建Bool来控制吗?而且太笨了。。。
//伪代码
方向{
东 1 >> 0 //0001
南 1 >> 1 //0010
西 1 >> 2 //0100
北 1 >> 3 //1000
}
东:0001, 南:0010,西:0100,北:1000
东南:0011,西南:0110,西北:1100,东北:1001
只需要使用半个字节-4位
就可以清楚描述这些信息。
在OC底层探索03一文中的alloc创建步骤3initInstanceIsa
中提到了isa值的创建
。通过查看iSA值的创建过程
找到我们想要的答案。
//isa的类型
union isa_t {
isa_t() { }
Class cls;
uintptr_t bits; //自定义类信息会存在这里
struct {
// isa值的内容
ISA_BITFIELD;
};
};
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
...
//只放出核心代码
isa_t newisa(0);
newisa.bits = ISA_MAGIC_VALUE;
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
isa = newisa;
}
使用ISA_BITFIELD
对isa的值进行了约定。
Style_月月-简书
根据位域
的知识,再来看这幅图中的结构,有木有豁然开朗。
从上至下,对应二进制的从低位到高位。每一位都存满了各种类的信息
。需要的注意shiftcls
这个位置,这个位置存储的就是类的信息,就是通过这个位置的信息,将isa和类建立了联系
。
现在知道了isa中都有些什么信息。可是口说无凭,下面就来验证一下。
0x001d8001000033bd
这个值就是isa。但是需要特别注意的是这个值并不是指针地址,它就是一个十六进制的值。
这个点对本文的理解很重要。lldb调试的一些常用命令 p 输出基本类型 p/t 输出二进制 p/x 输出十六进制 po 调用基本的description方法 x 打印十六进制地址 x/4gx 将十六进制分组方便观察,并打印4组
这个末尾指针是有规律的,后3位永远都是0.(这个是通过多次试验得出,如果有问题或者知道如何验证,希望不吝赐教)
需要注意isa结构中类的信息是从第4位开始的,只要将isa的后3位改为0
就可以直接得到类信息,所以在保存的时候需要将类指针进行位移(uintptr_t)cls >> 3
;这种设计太巧妙了!!!根据对ISA_BITFIELD
的观察,shiftcls前有3位,后有17位。将这些位置都置为0
,就可以得到isa中类的信息
验证方法一
这就是一个简单的位移运算,如果不明白自己动手试一下就知道了。
想要将这些位置改为0,当然也可以使用&
运算
//__arm64__
define ISA_MASK 0x0000000ffffffff8ULL
//__x86_64__
define ISA_MASK 0x00007ffffffffff8ULL
这就是apple提供的掩码
。也被称为isa的面具
。
验证方法二
验证过程相对简单,这种方式也比较常用。
apple工程师使用了位域的技术,在isa中保存了类的很多信息。这也是一种对于内存的优化。当然类里的其他信息-方法、属性,会在下文中进行解释。
再一次感叹apple工程师的强大,致敬!!!