前言
- 探索了一陣子的OC底層原理之后,我們通過下面幾個面試題復習一下。
目錄
- Runtime是什么?
- 方法的本質,sel是什么?IMP是什么?兩者之間的關系又是什么?
- 方法的調用順序
- Runtime是如何實現(xiàn)weak的,為什么可以自動置nil
- 能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量
- Runtime Asssociate方法關聯(lián)的對象,需要在dealloc中釋放?
- [self class]和[super class]的區(qū)別?
- 內存平移問題
一、Runtime是什么?
- runtime是由C和C++匯編實現(xiàn)的一套API,為OC語言加入了 面向對象、以及運行時的功能
- 平時編寫的OC代碼,在程序運行的過程中,其實最終會轉換成runtime的C語言代碼, runtime是OC的幕后工作者
- 運行時是指將數(shù)據(jù)類型的確定由編譯時 推遲到了 運行時
舉例:category分類,添加屬性是無用的,調用屬性會crash,但是通過runtime動態(tài)關聯(lián)對象,重寫setter、getter方法的方式就可以實現(xiàn)分類的屬性。
二、方法的本質,sel是什么?IMP是什么?兩者之間的關系又是什么?
-
方法的本質:發(fā)送消息,消息會有以下幾個流程
快速查找(objc_msgSend) - cache_t緩存消息中查找
慢速查找 - 遞歸自己|父類 - lookUpImpOrForward
查找不到消息:動態(tài)方法解析 - resolveInstanceMethod
消息快速轉發(fā) - forwardingTargetForSelector
消息慢速轉發(fā) - methodSignatureForSelector & forwardInvocation
sel是方法編號 - 在read_images期間就編譯進了內存 ,sel 相當于 一本書的目錄title
imp是函數(shù)實現(xiàn)指針 ,找imp就是找函數(shù)的過程 ,imp 相當于 書本的頁碼
-
查找具體的函數(shù)就是想看這本書具體篇章的內容
- 1、首先知道想看什么,即目錄 title - sel
- 2、根據(jù)目錄找到對應的頁碼 - imp
- 3、通過頁碼去翻到具體的內容
三、方法的調用順序
類的方法 和 分類方法 重名,如果調用,是什么情況?
-
如果同名方法是普通方法,包括initialize -- 先調用分類方法
- 因為分類的方法是在類realize之后 attach進去的,插在類的方法的前面,所以優(yōu)先調用分類的方法(注意:不是分類覆蓋主類?。。?/li>
- initialize方法什么時候調用? initialize方法也是主動調用,即第一次消息時調用,為了不影響整個load,可以將需要提前加載的數(shù)據(jù)寫到initialize中
如果同名方法是load方法 -- 先 主類load,后分類load(分類之間,看編譯的順序)
四、 Runtime是如何實現(xiàn)weak的,為什么可以自動置nil
初步回答:
runtime 對注冊的類會進行布局,對于 weak 修飾的對象會放入一個 hash 表(弱引用表)中。 用 weak 指向的對象內存地址作為key,當此對象的引用計數(shù)為0的時候會 dealloc,假如 weak 指向的對象內存地址是a,那么就會以a為鍵, 在這個 weak表中搜索,找到所有以a為鍵的 weak 對象,從而設置為 nil。-
細致回答:
- 1.初始化時:runtime會調用objc_initWeak函數(shù),初始化一個新的weak指針指向對象的地址。
- 2.添加引用時:objc_initWeak函數(shù)會調用objc_storeWeak() 函數(shù), objc_storeWeak()的作用是更新指針指向,創(chuàng)建對應的弱引用表。
- 3.釋放時,調用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak 指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設為nil,最后把這個entry從weak表中刪除,最后清理對象的記錄。
當weak引用指向的對象被釋放時,如何去處理weak指針?
1、調用objc_release
2、因為對象的引用計數(shù)為0,所以執(zhí)行dealloc
3、在dealloc中,調用了_objc_rootDealloc函數(shù)
4、在_objc_rootDealloc中,調用了object_dispose函數(shù)
5、調用objc_destructInstance
6、最后調用objc_clear_deallocating簡單來說:
a. 從weak表中獲取被釋放對象的地址為鍵值的記錄
b. 將包含在記錄中的所有附有 weak修飾符變量的地址,賦值為 nil
c. 將weak表中該記錄刪除
d. 從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
五、能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量?
- 問題一: 不可以。 因為編譯好的實例變量存放的位置在ro,一旦編譯完成,內存結構就完全確定了,無法修改。
- 問題二:在register注冊前,可以添加。但是調用運行時register注冊后,就完成了內存的注入,內存結構確定了,無法修改。
六、Runtime Asssociate方法關聯(lián)的對象,需要在dealloc中釋放?
- 不會,因為在dealloc中會自動釋放掉關聯(lián)的對象,在關聯(lián)對象的時候,isa中記錄了是否有關聯(lián)對象。通過dealloc 釋放。執(zhí)行如下:objc_object::rootDealloc() -> object_dispose()->objc_destructInstance() ,在objc_destructInstance() 這里面會根據(jù)isa指針下的has_assoc標記來判斷是否有關聯(lián)對象,如果有會自動釋放的。
七、 [self class]和[super class]的區(qū)別?
- 主要是他們方法查找順序不一樣。[super class]中,其中super 是語法的 關鍵字,可以通過clang 看super的本質,這是編譯時的底層源碼,其中第一個參數(shù)是消息接收者,是一個__rw_objc_super結構
- [self class]就是發(fā)送消息 objc_msgSend,消息接收者是self,方法編號 class
- [super class] 本質就是objc_msgSendSuper,消息的接收者還是 self,方法編號 class,在運行時,底層調用的是_objc_msgSendSuper2
- 實際運行時,[super class]在匯編層執(zhí)行直接從superclass父類開始搜索,節(jié)約了一輪查找資源, objc_msgSendSuper2 會更快,直接跳過self的查找
