我们已经从底层熟悉了对象
、类
、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
:
这是对象在底层的实现模板。首元素是isa
(这里涉及到struct
结构的内存优化
,我们这里记住结论。isa
在objc_object
的首位元素即可)
相关知识: 内存优化
objc_class
在obj4
源码中搜索struct objc_class
:
从上图知道,在类的内存中,首地址表示isa
,superclass
在isa
后面,需要内存地址
偏移8位
获取。
梳理逻辑
探索路径:
1. 从对象开始探索
从teacher
对象
- 打印地址:
p/x teacher
- 获取isa指针:
x/g $0
- 从isa指针中取出类地址:
p/x 0x001d8001000021c9 >> 3 << 20 >> 17
- 打印类:
po 0x00000001000021c8
成功找到继承类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
成功找到HTTeacher
的父类HTPerson
为了一探究竟。我们一口气追根溯源
,往上层层找寻父类
- 寻找superclass地址:
p/x 0x0000000100002178 + 8
- 获取父类的isa指针:
x/g 0x0000000100002180
- 从isa指针中取出类地址:
p/x 0x0000000100333140 >> 3 << 20 >> 17
- 打印父类:
po 0x0000000100333140
成功找到HTPerson
的父类NSObject
再接再厉,往上溯源
- 寻找superclass地址:
p/x 0x0000000100333140 + 8
- 获取父类的isa指针: x/g 0x0000000100333148
- 从isa指针中取出类地址:
p/x 0x0000000000000000 >> 3 << 20 >> 17
- 打印父类:
po 0x0000000000000000
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
成功找到HTTeacher元类
HTTeacher
类地址是:0x00000001000021c8
HTTeacher元类
地址是:0x00000001000021a0
我们接着往下。打印过程都一样。这里直接上完整打印图:
我们发现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指向
和类的继承
关系图
再奉上我的备注图: