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

iOS 底層探索: 學(xué)習(xí)大綱 OC篇

前言

  • 探索了一陣子的OC底層原理之后,我們通過(guò)下面幾個(gè)面試題復(fù)習(xí)一下。

目錄

    1. Runtime是什么?
    1. 方法的本質(zhì),sel是什么?IMP是什么??jī)烧咧g的關(guān)系又是什么?
    1. 方法的調(diào)用順序
    1. Runtime是如何實(shí)現(xiàn)weak的,為什么可以自動(dòng)置nil
    1. 能否向編譯后得到的類(lèi)中增加實(shí)例變量?能否向運(yùn)行時(shí)創(chuàng)建的類(lèi)中添加實(shí)例變量
    1. Runtime Asssociate方法關(guān)聯(lián)的對(duì)象,需要在dealloc中釋放?
    1. [self class]和[super class]的區(qū)別?
    1. 內(nèi)存平移問(wèn)題

一、Runtime是什么?

  • runtime是由C和C++匯編實(shí)現(xiàn)的一套API,為OC語(yǔ)言加入了 面向?qū)ο?、以及運(yùn)行時(shí)的功能
  • 平時(shí)編寫(xiě)的OC代碼,在程序運(yùn)行的過(guò)程中,其實(shí)最終會(huì)轉(zhuǎn)換成runtime的C語(yǔ)言代碼, runtime是OC的幕后工作者
  • 運(yùn)行時(shí)是指將數(shù)據(jù)類(lèi)型的確定由編譯時(shí) 推遲到了 運(yùn)行時(shí)

舉例:category分類(lèi),添加屬性是無(wú)用的,調(diào)用屬性會(huì)crash,但是通過(guò)runtime動(dòng)態(tài)關(guān)聯(lián)對(duì)象,重寫(xiě)setter、getter方法的方式就可以實(shí)現(xiàn)分類(lèi)的屬性。

二、方法的本質(zhì),sel是什么?IMP是什么??jī)烧咧g的關(guān)系又是什么?

  • 方法的本質(zhì):發(fā)送消息,消息會(huì)有以下幾個(gè)流程

    • 快速查找(objc_msgSend) - cache_t緩存消息中查找

    • 慢速查找 - 遞歸自己|父類(lèi) - lookUpImpOrForward

    • 查找不到消息:動(dòng)態(tài)方法解析 - resolveInstanceMethod

    • 消息快速轉(zhuǎn)發(fā) - forwardingTargetForSelector

    • 消息慢速轉(zhuǎn)發(fā) - methodSignatureForSelector & forwardInvocation

  • sel是方法編號(hào) - 在read_images期間就編譯進(jìn)了內(nèi)存 ,sel 相當(dāng)于 一本書(shū)的目錄title

  • imp是函數(shù)實(shí)現(xiàn)指針 ,找imp就是找函數(shù)的過(guò)程 ,imp 相當(dāng)于 書(shū)本的頁(yè)碼

  • 查找具體的函數(shù)就是想看這本書(shū)具體篇章的內(nèi)容

    • 1、首先知道想看什么,即目錄 title - sel
    • 2、根據(jù)目錄找到對(duì)應(yīng)的頁(yè)碼 - imp
    • 3、通過(guò)頁(yè)碼去翻到具體的內(nèi)容

三、方法的調(diào)用順序

類(lèi)的方法 和 分類(lèi)方法 重名,如果調(diào)用,是什么情況?

  • 如果同名方法是普通方法,包括initialize -- 先調(diào)用分類(lèi)方法

    • 因?yàn)榉诸?lèi)的方法是在類(lèi)realize之后 attach進(jìn)去的,插在類(lèi)的方法的前面,所以優(yōu)先調(diào)用分類(lèi)的方法(注意:不是分類(lèi)覆蓋主類(lèi)?。。?/li>
    • initialize方法什么時(shí)候調(diào)用? initialize方法也是主動(dòng)調(diào)用,即第一次消息時(shí)調(diào)用,為了不影響整個(gè)load,可以將需要提前加載的數(shù)據(jù)寫(xiě)到initialize中
  • 如果同名方法是load方法 -- 先 主類(lèi)load,后分類(lèi)load(分類(lèi)之間,看編譯的順序)

四、 Runtime是如何實(shí)現(xiàn)weak的,為什么可以自動(dòng)置nil

  • 初步回答:
    runtime 對(duì)注冊(cè)的類(lèi)會(huì)進(jìn)行布局,對(duì)于 weak 修飾的對(duì)象會(huì)放入一個(gè) hash 表(弱引用表)中。 用 weak 指向的對(duì)象內(nèi)存地址作為key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì) dealloc,假如 weak 指向的對(duì)象內(nèi)存地址是a,那么就會(huì)以a為鍵, 在這個(gè) weak表中搜索,找到所有以a為鍵的 weak 對(duì)象,從而設(shè)置為 nil。

  • 細(xì)致回答:

    • 1.初始化時(shí):runtime會(huì)調(diào)用objc_initWeak函數(shù),初始化一個(gè)新的weak指針指向?qū)ο蟮牡刂贰?/li>
    • 2.添加引用時(shí):objc_initWeak函數(shù)會(huì)調(diào)用objc_storeWeak() 函數(shù), objc_storeWeak()的作用是更新指針指向,創(chuàng)建對(duì)應(yīng)的弱引用表。
    • 3.釋放時(shí),調(diào)用clearDeallocating函數(shù)。clearDeallocating函數(shù)首先根據(jù)對(duì)象地址獲取所有weak 指針地址的數(shù)組,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)entry從weak表中刪除,最后清理對(duì)象的記錄。

當(dāng)weak引用指向的對(duì)象被釋放時(shí),如何去處理weak指針?

1、調(diào)用objc_release
2、因?yàn)閷?duì)象的引用計(jì)數(shù)為0,所以執(zhí)行dealloc
3、在dealloc中,調(diào)用了_objc_rootDealloc函數(shù)
4、在_objc_rootDealloc中,調(diào)用了object_dispose函數(shù)
5、調(diào)用objc_destructInstance
6、最后調(diào)用objc_clear_deallocating

簡(jiǎn)單來(lái)說(shuō):
a. 從weak表中獲取被釋放對(duì)象的地址為鍵值的記錄
b. 將包含在記錄中的所有附有 weak修飾符變量的地址,賦值為 nil
c. 將weak表中該記錄刪除
d. 從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄

五、能否向編譯后得到的類(lèi)中增加實(shí)例變量?能否向運(yùn)行時(shí)創(chuàng)建的類(lèi)中添加實(shí)例變量?

  • 問(wèn)題一: 不可以。 因?yàn)榫幾g好的實(shí)例變量存放的位置在ro,一旦編譯完成,內(nèi)存結(jié)構(gòu)就完全確定了,無(wú)法修改。
  • 問(wèn)題二:在register注冊(cè)前,可以添加。但是調(diào)用運(yùn)行時(shí)register注冊(cè)后,就完成了內(nèi)存的注入,內(nèi)存結(jié)構(gòu)確定了,無(wú)法修改。

六、Runtime Asssociate方法關(guān)聯(lián)的對(duì)象,需要在dealloc中釋放?

  • 不會(huì),因?yàn)樵赿ealloc中會(huì)自動(dòng)釋放掉關(guān)聯(lián)的對(duì)象,在關(guān)聯(lián)對(duì)象的時(shí)候,isa中記錄了是否有關(guān)聯(lián)對(duì)象。通過(guò)dealloc 釋放。執(zhí)行如下:objc_object::rootDealloc() -> object_dispose()->objc_destructInstance() ,在objc_destructInstance() 這里面會(huì)根據(jù)isa指針下的has_assoc標(biāo)記來(lái)判斷是否有關(guān)聯(lián)對(duì)象,如果有會(huì)自動(dòng)釋放的。

七、 [self class]和[super class]的區(qū)別?

  • 主要是他們方法查找順序不一樣。[super class]中,其中super 是語(yǔ)法的 關(guān)鍵字,可以通過(guò)clang 看super的本質(zhì),這是編譯時(shí)的底層源碼,其中第一個(gè)參數(shù)是消息接收者,是一個(gè)__rw_objc_super結(jié)構(gòu)
  • [self class]就是發(fā)送消息 objc_msgSend,消息接收者是self,方法編號(hào) class
  • [super class] 本質(zhì)就是objc_msgSendSuper,消息的接收者還是 self,方法編號(hào) class,在運(yùn)行時(shí),底層調(diào)用的是_objc_msgSendSuper2
  • 實(shí)際運(yùn)行時(shí),[super class]在匯編層執(zhí)行直接從superclass父類(lèi)開(kāi)始搜索,節(jié)約了一輪查找資源, objc_msgSendSuper2 會(huì)更快,直接跳過(guò)self的查找
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • OC底層原理 學(xué)習(xí)大綱[http://www.itdecent.cn/p/9e19354c0266] 目錄: 關(guān)...
    markhetao閱讀 651評(píng)論 0 2
  • 前言 我們類(lèi)的底層探索已經(jīng)告一段落,我們梳理一下常見(jiàn)的面試題,希望對(duì)你有些幫助。 問(wèn)題 1.runtime是什么?...
    Y丶舜禹閱讀 1,071評(píng)論 0 2
  • 1.我們關(guān)聯(lián)的對(duì)象是否需要手動(dòng)移除,為什么 不需要手動(dòng)移除,在對(duì)象的dealloc中在關(guān)聯(lián)對(duì)象時(shí),如果是第一次,我...
    MonKey_Money閱讀 539評(píng)論 0 0
  • 面試題-1 Runtime Asssociate方法關(guān)聯(lián)的對(duì)象,需要在dealloc中釋放? 當(dāng)我們對(duì)象釋放時(shí),會(huì)...
    含笑州閱讀 354評(píng)論 0 1
  • iOS 底層原理 文章匯總[http://www.itdecent.cn/p/412b20d9a0f6] 【面試...
    Style_月月閱讀 3,828評(píng)論 6 12

友情鏈接更多精彩內(nèi)容