前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >isa 指针走向

isa 指针走向

作者头像
CC老师
发布2022-03-15 08:09:17
5390
发布2022-03-15 08:09:17
举报

作为一个 iOSer, 都知道NSObject 是基类, 肯定都听说过一句话: 万物皆对象, NSObject 类的第一个成员就是 isa 指针, 这个就不展示源码了, 这个指针存着类的很多信息, 而不仅仅是指向类内存的指针.

isa 定义

isa 指针的底层原本定义如下, 只看成员, 不看方法;

__arm64__ 真机的宏定义

代码语言:javascript
复制
#     define ISA_MASK        0x0000000ffffffff8ULL
#     define ISA_MAGIC_MASK  0x000003f000000001ULL
#     define ISA_MAGIC_VALUE 0x000001a000000001ULL
#     define ISA_HAS_CXX_DTOR_BIT 1
#     define ISA_BITFIELD                                                      \
uintptr_t nonpointer        : 1;                                       \
        uintptr_t has_assoc         : 1;                                       \
        uintptr_t has_cxx_dtor      : 1;                                       \
        uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
        uintptr_t magic             : 6;                                       \
        uintptr_t weakly_referenced : 1;                                       \
        uintptr_t unused            : 1;                                       \
        uintptr_t has_sidetable_rc  : 1;                                       \
        uintptr_t extra_rc          : 19
#     define RC_ONE   (1ULL<<45)
#     define RC_HALF  (1ULL<<18)

(滑动显示更多)

__x86_64__ macOS 的宏定义

代码语言:javascript
复制
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_HAS_CXX_DTOR_BIT 1
#   define ISA_BITFIELD                                                        \
uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t unused            : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

(滑动显示更多)

代码语言:javascript
复制
union isa_t {
uintptr_t bits;
private:
    Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
        ISA_BITFIELD; 
    };
#endif
};

(滑动显示更多)

原本的定义比较不太好看, 我给他改造一下, 整合在一起, 以 macOS 为例, 下面将会用 macOS 项目进行举例, shiftcls 就是指向类的信息.

这个值怎么取呢?

只要让 isa 的值 和 ISA_MASK 进行 与 运算即可.

代码语言:javascript
复制
union isa_t {
uintptr_t bits;
    Class cls;
struct {
uintptr_t nonpointer        : 1;                                         
uintptr_t has_assoc         : 1;                                         
uintptr_t has_cxx_dtor      : 1;                                         
uintptr_t shiftcls          : 44;
uintptr_t magic             : 6;                                         
uintptr_t weakly_referenced : 1;                                         
uintptr_t unused            : 1;                                         
uintptr_t has_sidetable_rc  : 1;                                         
uintptr_t extra_rc          : 8; 
    };
};

(滑动显示更多)

isa指针 和 继承关系的走向图

举例来验证 isa 指针走向

接下来我们以自定义类举例来验证这幅图的 isa 指针走向, 我们一步一步向上查找.

首先我们创建一个 macOS 平台的 Command Line Tool 项目

创建两个类, 分别是 Person Teacher, Teacher继承自Person.

主要代码如下, 在 Hello world 这一行下一个断点. 然后进行 lldb 调试, 其实这些打印也没什么用, 也可以用 lldb 输出, 这里就对比打印结果就可以. 图和代码都给出来, 图比较直观.

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
@autoreleasepool {

        Teacher *teacher = [Teacher alloc];

NSLog(@"%@",teacher);
NSLog(@"%@ - %p",teacher.class, teacher.class);
NSLog(@"%@ - %p",[Teacher class], [Teacher class]);
NSLog(@"%@ - %p",object_getClass(teacher), object_getClass(teacher));

NSLog(@"Hello world !");
    }
return 0;
}

(滑动显示更多)

代码图和类信息的输出

1.读取对象的内存并格式化输出, 然后查看类信息.

  • 重要的命令
代码语言:javascript
复制
$ x/4gx teacher
$ p/x 0x011d800100008189 & 0x00007ffffffffff8ULL
$ po 0x0000000100008160

(滑动显示更多)

2.读取类的内存并格式化输出, 然后查看元类信息.

3.读取类的内存并格式化输出, 然后查看根元类信息.

4.读取类的内存并格式化输出, 然后查看根元类信息.

此时发现根元类的 isa 是指向自己的, 也就意味着 isa 走到头了.

继承关系走向

看看各自的继承关系走向, NSObject 第二个成员就是 superClass, 也就是内存的第二段, 从打印中可以看出, 不仅所有的普通类最终继承自 NSObject, 连 根元类 也继承自 NSObject, 我想这也是 万物皆对象 这名话的意义吧

特殊情况:读取基类 NSObject 的内存信息, 会发现父类是 nil, isa 也指向 根元类.

总结

我对这幅图理解有两点, 简单说一个是关于 isa 指针的, 一个是关于继承关系的, 继承关系这个大家应该比较熟悉, 因为接触的比较多, 几乎每天都在打交道, isa 指针就不同了, 因为平时也用不上, 属于底层原理级别的.

1.isa指针走向, 根元类的isa指向自己

  • 对象 -> 类 -> 元类 -> 根元类 <-> 根元类
  • 特殊: NSObject 对象 -> NSObject类 -> NSObject元类(根元类) <-> 根元类

2.继承关系走向, 根元类的父类指向 NSObject, 这就是万物皆对象

  • 子类 -> 父类 -> 父类的父类 -> ... -> NSObject -> nil
  • 子元类 -> 父元类 -> 父元类的父类 -> ... -> 根元类 -> NSObject -> nil

特别注意的是 isa 只会从对象找到类, 再到元类, 然后直接到根元类.

不仅类之间存在继承关系, 元类之间也存在继承关系.

类在内存里只存在一份, 继承关系只存在于类之间, 而不存在于对象之间;

到此, 我们的验证也结束了. 相信大家会对这幅图有一个全新的认识, 对 isa 指针也会有一个全新的认识. 感谢捧场, 来都来了, 点赞支持一下吧.

作者:Andy_GF

链接:https://juejin.cn/post/7043717006901641229

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-01-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 HelloCoder全栈小集 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • isa 定义
  • isa指针 和 继承关系的走向图
  • 举例来验证 isa 指针走向
  • 代码图和类信息的输出
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档