我们已经从底层熟悉了对象、类、isa。但碎片化的知识让我有点头晕。 学着学着发现,我不知道如何用语言来完整的描述他们了。
为了避免造成邯郸学步的惨剧。
现在,我将以上帝视角来梳理一下他们之间的关系。
如果你准备好了,我们就开始吧~
前期准备
在main.m文件中加入测试代码
-
HTTeacher继承自HTPerson,HTPerson继承自NSObject -
teacher是HTTeacher实例化对象
@interface HTPerson : NSObject
@end
@implementation HTPerson
@end
@interface HTTeacher: HTPerson
@end
@implementation HTTeacher
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
HTTeacher * teacher = [[HTTeacher alloc]init];
NSLog(@"%p", teacher);
}
return 0;
}
在NSLog位置加入断点。
objc4 源码
objc4 源码下载-> objc4-781.tar.gz
objc_object
在obj4源码中搜索struct objc_object:
image.png
这是对象在底层的实现模板。首元素是isa(这里涉及到struct结构的内存优化,我们这里记住结论。isa在objc_object的首位元素即可)
相关知识: 内存优化
objc_class
在obj4源码中搜索struct objc_class:
image.png
从上图知道,在类的内存中,首地址表示isa,superclass在isa后面,需要内存地址偏移8位获取。
梳理逻辑
image.png
探索路径:
1. 从对象开始探索
从teacher对象
- 打印地址:
p/x teacher - 获取isa指针:
x/g $0 - 从isa指针中取出类地址:
p/x 0x001d8001000021c9 >> 3 << 20 >> 17 - 打印类:
po 0x00000001000021c8
image.png
成功找到继承类HTTeacher
2 寻找父类源头:
查看上面准备的
objc_class结构,父类首地址是isa指针,占用了8字节。
- 所以我们从类地址
偏移8位就可以找到superclass类。
- 找到superclass地址:
p/x 0x00000001000021c8 + 8 - 获取父类的isa指针:
x/g 0x00000001000021d0 - 从isa指针中取出类地址:
p/x 0x0000000100002178 >> 3 << 20 >> 17 - 打印父类:
po 0x0000000100002178
image.png
成功找到HTTeacher的父类HTPerson
为了一探究竟。我们一口气追根溯源,往上层层找寻父类
- 寻找superclass地址:
p/x 0x0000000100002178 + 8 - 获取父类的isa指针:
x/g 0x0000000100002180 - 从isa指针中取出类地址:
p/x 0x0000000100333140 >> 3 << 20 >> 17 - 打印父类:
po 0x0000000100333140
image.png
成功找到HTPerson的父类NSObject
再接再厉,往上溯源
- 寻找superclass地址:
p/x 0x0000000100333140 + 8 - 获取父类的isa指针: x/g 0x0000000100333148
- 从isa指针中取出类地址:
p/x 0x0000000000000000 >> 3 << 20 >> 17 - 打印父类:
po 0x0000000000000000
image.png
NSObjet 的父类是nil(0x0000000000000000)。 到达尽头!
所以我们说OC中,想要确定类的继承关系,找到NSObject就可以停止了。因为已经到尽头了!
附上完整截图:
类的继承
3. 寻找isa的源头:
isa相比于类而言,不需要进行偏移。但是需要准确找到类中isa的地址
查看上面准备的objc_class结构,父类首地址是isa指针。
- 我们应该用x/g打印获取isa的准确内存位置
- 从isa中获取类的地址,我们需要准确截取
shiftcls部分。
(我一般使用内存地址>>3 << 20 >> 17, 你们也可以&与上ISA_MASK0x00007ffffffffff8ULL)
- 获取类的isa指针
x/g 0x00000001000021c8 - 从Isa指针中取出类地址:
p/x 0x00000001000021a0 >> 3 << 20 >> 17 - 打印类名:
po 0x00000001000021a0
image.png
成功找到HTTeacher元类
HTTeacher类地址是:0x00000001000021c8HTTeacher元类地址是:0x00000001000021a0
我们接着往下。打印过程都一样。这里直接上完整打印图:
isa指向图
我们发现2个情况:
-
HTTeacher的isa首先指向HTTeacher的元类,之后直接指向了NSObject元类。与HTTeacher的父类HTPerson完全无关 -
NSObject元类的isa指针指向的是NSObject元类。
1. 如何确定是NSObject元类,而不是NSObjet自身类?
image.png
我们打印NSObject自身类和NSObject元类地址
- 与上面地址
0x00000001003330f0进行对比。 可以肯定上面打印的是NSObject元类地址
2.
NSObject元类也是类,那它有父类(superclass)吗?
image.png
- 通过
指针偏移8位,到达superclass地址。- 打印发现
NSObject元类有父类,它的父类指向了NSObject本类。
3.
NSObject元类有父类, 那其他元类(比如HTTeacher元类)有父类吗?
image.png
曾经的我以为打印不出来,那就是
其他元类都没有父类咯?
聪明的你看出错误了吗😂image.png
总结:
-
对象内的isa指针指向自己的类.
-
-
类有完整的继承关系(每个类都通过superclass记录自己的父类,层层溯源)
-
-
根类是NSObject
NSObject的父类为nil,所以NSObject无父类
所有类的最终父类都是NSObject
-
-
isa的指向与类的继承无关
-
- 任何类
isa都是先指向自己元类,再指向NSObject元类。
(除了NSObject类, 因为他自己元类就是NSObject元类)
- 任何类
-
根元类是NSObject元类。
NSObject元类的isa指向永远都是自己
-
-
元类也有父类,根元类(NSObject元类)的父类(superclass)是NSObject类
-
奉上经典的isa指向和类的继承关系图
isa和类继承流程图.png
再奉上我的备注图:
isa和类继承流程图.png

类的继承
image.png
image.png
image.png
image.png











网友评论