我們已經從底層熟悉了對象
、類
、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指向
和類的繼承
關系圖
再奉上我的備注圖: