我們已經從底層熟悉了對象、類、isa。但碎片化的知識讓我有點頭暈。 學著學著發(fā)現,我不知道如何用語言來完整的描述他們了。
為了避免造成邯鄲學步的慘劇。
現在,我將以上帝視角來梳理一下他們之間的關系。
如果你準備好了,我們就開始吧~
前期準備
在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結構的內存優(yōu)化,我們這里記住結論。isa在objc_object的首位元素即可)
相關知識: 內存優(yōu)化
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字節(jié)。
- 所以我們從類地址
偏移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類地址是:0x00000001000021c8HTTeacher元類地址是:0x00000001000021a0
我們接著往下。打印過程都一樣。這里直接上完整打印圖:

我們發(fā)現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地址。- 打印發(fā)現
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指向和類的繼承關系圖

再奉上我的備注圖:





