iOS 底層探索:OC底層面試題分析(會持續(xù)補充)

iOS 底層探索: 學習大綱 OC篇

前言

  • 探索了一陣子的OC底層原理之后,我們通過下面幾個面試題復習一下。

目錄

    1. Runtime是什么?
    1. 方法的本質,sel是什么?IMP是什么?兩者之間的關系又是什么?
    1. 方法的調用順序
    1. Runtime是如何實現(xiàn)weak的,為什么可以自動置nil
    1. 能否向編譯后得到的類中增加實例變量?能否向運行時創(chuàng)建的類中添加實例變量
    1. Runtime Asssociate方法關聯(lián)的對象,需要在dealloc中釋放?
    1. [self class]和[super class]的區(qū)別?
    1. 內存平移問題

一、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的查找
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容