平時看到過一些關於Objective-C的底層的文章和書籍,在這記錄一下一些重要的知識點。
一、本質
在runtime庫中,對象是用C語言中的結構體表示的,用C/C++和彙編編寫實現的。
Objective-C --> C/C++ --> 彙編語言 --> 機器語言
在Xcode中一層一層點到NSObject的內部實現時候,會看到它是由Class結構體來實現的:
Class isa其實就是一個objc_class結構體指針:
objc_class的結構體內容如下:
- isa:對象需要通過isa指針找到它的類,類需要通過isa找到它的元類。
- super_class:指向該類的父類,如果是最頂層的類,則其是nil。
- cache:用於緩存類中最近使用的方法。(查找方法時先看cache裏有沒有再去methodLists找,因爲如果多個方法只有一個被調用,每次遍歷list效率低)
- ivars:指向該類的成員變量鏈表。
- methodLists:指向方法定義的鏈表。
- protocols:指向協議鏈表。
二、內存大小
從上面NSObject的內部實現看,它只有一個isa指針,而指針在64位架構中佔8個字節。也就是說NSObjcet實際上是隻有一個名爲isa的指針的結構體,因此佔用一個指針變量所佔用的內存空間大小,如果64bit佔用8個字節,如果32bit佔用4個字節。
如果是NSObject的子類,增加了屬性,比如兩個int類型,isa指針8個字節+int類型4個字節+int類型4個字節共16個字節。
如果是NSObject的子類只增加了一個int類型屬性,則8+4是12個字節,但實際上是佔用了16個字節。
原因是編譯器在給結構體開闢空間時,首先找到結構體中最寬的基本數據類型,然後尋找內存地址能是該基本數據類型的整倍的位置,作爲結構體的首地址。將這個最寬的基本數據類型的大小作爲對齊模數。爲結構體的一個成員開闢空間之前,編譯器首先檢查預開闢空間的首地址相對於結構體首地址的偏移是否是本成員的整數倍,若是則存放本成員,反之則在本成員和上一個成員之間填充一定的字節,以達到整數倍的要求,也就是將預開闢空間的首地址後移幾個字節。
內存對齊爲兩個原則:
原則 1. 前面的地址必須是後面的地址正數倍,不是就補齊。
原則 2. 整個Struct的地址必須是最大字節的整數倍。
所以對於上面第二個情況不滿足原則2,應該需要補齊4個字節,所以也是18個字節。
三、屬性和方法的存放
在iOS的環境中一共有三種對象:instance對象(實例對象)、class對象(類對象)和meta-class對象(元類對象)。
1、instance對象就是通過類alloc出來的對象,每次調用alloc都會產生新的instance對象,不同的對象佔用不同的內存,instance對象在內存中存儲的信息包括isa指針和其他成員變量。instance對象的isa指針指向class對象。
2、我們通過class方法或runtime方法得到一個class對象。class對象也就是類對象,每一個類在內存中有且只有一個class對象。class對象的isa指針指向meta-class對象。class對象在內存中存儲的信息主要包括:
- isa指針
- superclass指針
- 類的屬性信息(@property),類的成員變量信息(ivar)
- 類的對象方法信息(instance method),類的協議信息(protocol)
3、每個類在內存中有且只有一個meta-class對象。meta-class對象和class對象的內存結構是一樣的,但是用途不一樣。meta-class對象的isa指針指向基類的meta-class對象,基類自己的isa指針也指向自己。在內存中存儲的信息主要包括:
- isa指針
- superclass指針
- 類的類方法的信息(class method)
所以,成員變量的具體值存放在instance對象。對象方法,協議,屬性,成員變量信息存放在class對象。類方法信息存放在meta-class對象。對象、類、父類和基類的關係如下圖所示:
對isa、superclass總結
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基類的meta-class,基類的isa指向自己
- class的superclass指向父類的class,如果沒有父類,superclass指針爲nil
- meta-class的superclass指向父類的meta-class,基類的meta-class的superclass指向基類的class
- instance調用對象方法的軌跡,isa找到class,方法不存在,就通過superclass找父類
- class調用類方法的軌跡,isa找meta-class,方法不存在,就通過superclass找父類
四、消息傳遞
根據上述對象、類、父類和基類的關係,消息的傳遞機制大題如下:
系統首先找到消息的接收對象,然後通過對象的isa找到它的類。在它的類中先查找cache再查找method_list,是否有selector方法。沒有則查找父類super_class的method_list。SEL實際上是根據方法名Hash轉換的一個字符串,找到對應的method,執行它的IMP。