前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OC底层探索06-isa本身藏了多少信息你知道吗?OC底层探索06-isa本身藏了多少信息你知道吗?

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

作者头像
用户8893176
发布2021-08-09 11:27:15
3530
发布2021-08-09 11:27:15
举报
文章被收录于专栏:小黑娃Henry

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

查看Clang编译文件

代码语言:javascript
复制
//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. 类里都有些什么?
  • 看到struct进一步验证了类是一个结构体。(之struct NSObject_IMPL NSObject_IVARS前,文章中提到类是继承结构体objc_object万物之源)
  • 结构体内部除了有struct NSObject_IMPL NSObject_IVARS成员变量:HRTestName
    • 检查编译文件中所有的类都会有NSObject_IMPL这个参数,那它一定就是isa没跑了。
    • 成员变量编译之后和类放在一起,而属性编译后并不是变量而是以get,set方法的形式存在。

联合体、位域

联合体

因为在isa使用了一种位域技术,来保存内部信息,这里简单介绍一下联合体、位域

联合体(union):各变量是“互斥”的,同时只能有一个变量有值,且公用同一块内存。优点是内存使用更为精细灵活,也节省了内存空间。C语言共用体详解

位域

如果有一个需求,需要能表达东南西北四个方向。第一想到的就是创建4个Bool值来进行控制,可是4个Bool需要:4个字节

现在需求变了,需要还能表达东南,西南,西北,东北,继续创建Bool来控制吗?而且太笨了。。。

如果通过这样一个结构来描述呢?
代码语言:javascript
复制
//伪代码
方向{
    东 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值的创建过程找到我们想要的答案。

代码语言:javascript
复制
//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中找到类的信息,就可以验证之前的结论。
  • 这是当前类的指针.
  • 注意标红位置:这个末尾指针是有规律的,后3位永远都是0.(这个是通过多次试验得出,如果有问题或者知道如何验证,希望不吝赐教)需要注意isa结构中类的信息是从第4位开始的,只要将isa的后3位改为0就可以直接得到类信息,所以在保存的时候需要将类指针进行位移(uintptr_t)cls >> 3;这种设计太巧妙了!!!
验证方法一

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

验证方法一

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

验证方法二

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

代码语言:javascript
复制
//__arm64__
define ISA_MASK        0x0000000ffffffff8ULL
//__x86_64__
define ISA_MASK        0x00007ffffffffff8ULL

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

验证方法二

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

总结

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/9/14 上,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查看Clang编译文件
  • 联合体、位域
    • 联合体
      • 位域
        • 如果通过这样一个结构来描述呢?
        • 这就是位域技术:通过位运算,将每一位都放入信息。
        • 从上至下,对应二进制的从低位到高位。每一位都存满了各种类的信息。
        • 0x001d8001000033bd这个值就是isa。但是需要特别注意的是这个值并不是指针地址,它就是一个十六进制的值。这个点对本文的理解很重要。
    • isa指针
    • isa指针信息的LLDB验证
      • 只要能通过lldb的调试从isa中找到类的信息,就可以验证之前的结论。
        • 验证方法一
          • 验证方法二
          • 总结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档