OC底层原理09:isa走向&继承分析

张建 lol

本文的主要目的是分析 isa走向继承 的分析

准备工作

定义两个类 :

  • 继承自 NSObjectZJPerson
1
2
3
4
@interface ZJPerson : NSObject
@end
@implementation ZJPerson
@end
  • 继承自 ZJPersonZJStudent
1
2
3
4
@interface ZJStudent : ZJPerson
@end
@implementation ZJStudent
@end
  • main.m 中 分别创建两个对象:person & student
1
2
3
4
5
6
7
8
9
int main(int argc, const char * argv[]) {
@autoreleasepool {
// ISA_MASK 0x00007ffffffffff8ULL
ZJPerson * person = [ZJPerson alloc];
ZJStudent * student = [ZJStudent alloc];
NSLog(@"Hello World:%@ - %@",person,student);
}
return 0;
}

元类

首先,我们先通过一个案例的 lldb 调试引入 元类 概念

mainNSLog(@"Hello World:%@ - %@",person,student); 处下一个 断点,运行程序,开启 lldb 调试,调试的过程如下图所示:

  • x/4gx person :查看 person 的内存分布情况,拿到 isa 的指针地址 0x001d8001000021d1

  • p/x person :拿到 0x0000000101159370 地址,这个地址首先代表 首地址,其次代表 当前对象,也代表 isa

那么为什么即代表当前对象,又代表 isa 呢?

任何自定义的类,如 ZJPerson,均继承自 NSObjectNSObject 默认第一个参数为 Class isa,所以首地址会指向 isaisa 又代表当前的类

  • p/x 0x001d8001000021d1 & 0x00007ffffffffff8ULL:获取 类ZJPerson 的指针地址 0x00000001000021d0

  • po 0x00000001000021d0:根据 的指针地址 0x00000001000021d0 打印 类信息得到ZJPerson

  • 查看类的 内存分布 有三种方式:

    • 通过 指针地址: x 0x00000001000021d0
    • 通过 class 的 API: x ZJPerson.class
    • 通过 runtime 的 API: x object_getClass(person)
  • x/4gx 0x00000001000021d0:查看 类ZJPerson内存分布 情况,拿到 isa 指针地址 0x00000001000021a8

  • po 0x00000001000021a8:打印ZJPerson类isa 指针指向的内容,即 元类,其元类也是 ZJPerson

  • p/x 0x00000001000021a8 & 0x00007ffffffffff8ULL:通过 类isa & mask 获取 元类isa 指针地址 0x00000001000021a8

  • po 0x00000001000021a8:打印 元类 的指针信息,拿到 ZJPerson

根据调试过程,我们产生了一个疑问:为什么图中的 p/x 0x001d8001000021d1 & 0x00007ffffffffff8ULLp/x 0x00000001000021a8 & 0x00007ffffffffff8ULL 中的类信息打印出来都是 ZJPerson

  • 0x001d8001000021d1 是person对象的isa指针地址,其 & mask 后得到的结果是获取对象 person类ZJPerson

  • 0x00000001000021a8元类的isa指针地址,即 ZJPerson类的类 的isa指针地址,在Apple中,我们简称 ZJPerson类的类元类

  • 所以,两个打印都是 ZJPerson 的根本原因就是因为 元类 导致的

元类的说明:

下面来解释什么是元类,主要有以下几点说明:

  • 我们知道 对象的isa指向类 其实 也是 一个 对象,可以称为 类对象,其 isa 的位域 指向 苹果定义的 元类

  • 元类系统生成 的,其定义和创建都是由编译器完成,在这个过程中, 的归属 来自元类

  • 元类类对象,每个类都有一个独一无二的元类用来存储 类方法的相关信息。

  • 元类本身是没有名称的,由于与类相关联,所以使用了同类名一样的名称

isa走向

下面通过 lldb 命令来探索isa指针的走向,如下图所示:

由上图 lldb 探索可以得出一个关系链:对象 --> 类 --> 元类 --> NSobject --> 指向自己

下面用一张图表示 isa走向 流程:

由上面的分析结果我们可知:

  • 实例对象isa 指针指向
  • 类对象isa 指针指向
  • 元类isa 指针指向 根元类
  • 根元类isa 指针指向 自己

NSObject到底有几个?

由上图可知,最后的根元类是 NSObject,这和我们日常开发中所知道的 NSObject 是同一个吗?

有以下两种验证方式

  • 【方法一】lldb命令验证
  • 【方法二】代码验证
  1. 【方法一】lldb命令验证

从图中可以看出,由实例对象 person 最终 isa 找到的根源个 NSObject,与下面开发中使用的 NSobject 相同,所以可以得出一个结论:内存中只存在一份根元类NSObject,根元类的元类是指向它自己

  1. 【方法二】代码验证

通过三种不同的方式获取类,看他们打印的地址是否相同

1
2
3
4
5
6
7
// MARK: -分析类对象在内存中存在几份
void testClassNum(){
Class class1 = [ZJPerson class];
Class class2 = [ZJPerson alloc].class;
Class class3 = object_getClass([ZJPerson alloc]);
NSLog(@"\n%p-\n%p-\n%p", class1, class2, class3);
}

以上代码的运行结果是

1
2
3
4
2020-10-02 22:46:53.183944+0800 ZJObjc[21320:623345] 
0x100002190-
0x100002190-
0x100002190-

从打印结果中可以看出,打印的 地址 都是 同一个 ,所以 类对象 在内存中 只存在一份

【面试题】

问:类对象在内存中存在几份?

答:由于类信息在内存中只存在一份,所以 类对象只存在一份

继承

下面我们通过两道面试题来了解一下继承关系

1、【面试题1】:

我们知道 类ZJTeacher 继承自 类ZJPerson ,那么 实例对象tearher实例对象person 有关系么?

答:没有关系

分析:

  • 继承关系之来源与
  • 实例对象没有关系

2、【面试题2】:

ZJTearher 继承自 ZJPersonZJPerson 继承自 NSObject,那么 NSObject 继承自谁呢?

答:NSObject 继承自 nil

用一张图来总结一下继承关系:

ZJTeacher 和 NSObject isa 走位图

由上面 继承isa走位图 分析,我们可以得出 ZJTeacherNSObjectisa走位 图:

继承 和 isa走位图

根据上面的探索以及各种验证,我们可以得到著名的 继承 & isa走位图

isa走位链三条:

  • teacher(实例对象) -> ZJTeacher(类) -> ZJTeacher(元类)-> NSObject(根元类)-> NSObject(根根元类)其实就是 自己

  • person(实例对象) -> ZJPerson(类) -> ZJPerson(元类)-> NSObject(根元类)-> NSObject(根根元类)其实就是 自己

  • object(实例对象) -> NSObject(类) -> NSObject(元类)-> NSObject(根元类)-> NSObject(根根元类)其实就是 自己

继承链两条:

  • ZJTeacher(子类)-> ZJPerson(父类)-> NSObject(根类) -> nil

  • ZJTeacher(子元类)-> ZJPerson(父元类)-> NSObject(根元类) -> NSObject(根类)-> nil

  • Post title:OC底层原理09:isa走向&继承分析
  • Post author:张建
  • Create time:2020-09-22 21:57:52
  • Post link:https://redefine.ohevan.com/2020/09/22/OC底层原理/OC底层原理09:isa走向&继承分析/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.