iOS 开发每天分享优质文章专注iOS开发的小渣渣

OC底层探索06-isa本身藏了多少信息你知道吗?

2020-09-14  本文已影响0人  Henry________

一直都说类最终都会编译为struct,可是怎么验证呢?编译后的结构体内部都会有些什么东西呢?

查看Clang编译文件

//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。好长好长,但是无所谓我们关注的是:

  1. 类编译后是什么?
  2. 类里都有些什么?

联合体、位域

联合体

因为在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位就可以清楚描述这些信息。

这就是位域技术:通过位运算,将每一位都放入信息。

isa指针

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中都有些什么信息。可是口说无凭,下面就来验证一下。

isa指针信息的LLDB验证

0x001d8001000033bd这个值就是isa。但是需要特别注意的是这个值并不是指针地址,它就是一个十六进制的值。这个点对本文的理解很重要。
lldb调试的一些常用命令

p 输出基本类型
p/t 输出二进制
p/x 输出十六进制
po 调用基本的description方法
x 打印十六进制地址
x/4gx 将十六进制分组方便观察,并打印4组

只要能通过lldb的调试从isa中找到类的信息,就可以验证之前的结论。

验证方法一

根据对ISA_BITFIELD的观察,shiftcls前有3位,后有17位。将这些位置都置为0,就可以得到isa中类的信息

验证方法一

这就是一个简单的位移运算,如果不明白自己动手试一下就知道了。

验证方法二

想要将这些位置改为0,当然也可以使用&运算

//__arm64__
define ISA_MASK        0x0000000ffffffff8ULL
//__x86_64__
define ISA_MASK        0x00007ffffffffff8ULL

这就是apple提供的掩码。也被称为isa的面具

验证方法二

验证过程相对简单,这种方式也比较常用。

总结

apple工程师使用了位域的技术,在isa中保存了类的很多信息。这也是一种对于内存的优化。当然类里的其他信息-方法、属性,会在下文中进行解释。

再一次感叹apple工程师的强大,致敬!!!

上一篇 下一篇

猜你喜欢

热点阅读