OC底層原理:對象、類、isa 邏輯梳理

OC底層原理 學習大綱

我們已經從底層熟悉了對象isa。但碎片化的知識讓我有點頭暈。 學著學著發現,我不知道如何用語言來完整的描述他們了。

為了避免造成邯鄲學步的慘劇。

現在,我將以上帝視角來梳理一下他們之間的關系。

在了解之前,如果你還不了解isa結構類的原理。請先去了解一下。
?? isa結構
?? 類的原理

如果你準備好了,我們就開始吧~

前期準備

main.m文件中加入測試代碼

  • HTTeacher繼承自HTPersonHTPerson繼承自NSObject
  • teacherHTTeacher實例化對象
@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結構的內存優化,我們這里記住結論。isaobjc_object的首位元素即可)

相關知識: 內存優化

objc_class

obj4源碼中搜索struct objc_class:

image.png

從上圖知道,在類的內存中,首地址表示isasuperclassisa后面,需要內存地址偏移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_MASK 0x00007ffffffffff8ULL
  • 獲取類的isa指針 x/g 0x00000001000021c8
  • 從Isa指針中取出類地址: p/x 0x00000001000021a0 >> 3 << 20 >> 17
  • 打印類名: po 0x00000001000021a0
image.png

成功找到HTTeacher元類

  • HTTeacher類地址是:0x00000001000021c8
  • HTTeacher元類地址是:0x00000001000021a0

我們接著往下。打印過程都一樣。這里直接上完整打印圖:

isa指向圖

我們發現2個情況:

  1. HTTeacher的isa首先指向HTTeacher的元類,之后直接指向了NSObject元類。與HTTeacher的父類HTPerson完全無關
  2. 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

總結:

    1. 對象內的isa指針指向自己的類.
    1. 有完整的繼承關系(每個類都通過superclass記錄自己的父類,層層溯源)
    1. 根類NSObject
      NSObject的父類為nil,所以NSObject無父類
      所有類的最終父類都是NSObject
    1. isa的指向與類的繼承無關
    1. 任何類isa都是先指向自己元類,再指向NSObject元類
      (除了NSObject類, 因為他自己元類就是NSObject元類)
    1. 根元類NSObject元類
      NSObject元類isa指向永遠都是自己
    1. 元類也有父類,根元類(NSObject元類)的父類(superclass)是NSObject類

奉上經典的isa指向類的繼承關系圖

isa和類繼承流程圖.png

再奉上我的備注圖:


isa和類繼承流程圖.png

歡迎一起交流學習。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。