大廠常問iOS面試題--Runtime篇

1.Category 的實現原理?

  • Category 實際上是 Category_t的結構體,在運行時,新添加的方法,都被以倒序插入到原有方法列表的最前面,所以不同的Category,添加了同一個方法,執行的實際上是最後一個。

  • Category 在剛剛編譯完的時候,和原來的類是分開的,只有在程序運行起來後,通過 Runtime ,Category 和原來的類纔會合併到一起。

2.isa指針的理解,對象的isa指針指向哪裏?isa指針有哪兩種類型?

  • isa 等價於 is kind of

    實例對象的 isa 指向類對象

    類對象的 isa 指向元類對象

    元類對象的 isa 指向元類的基類

  • isa 有兩種類型

    純指針,指向內存地址

    NON_POINTER_ISA,除了內存地址,還存有一些其他信息

3.Objective-C 如何實現多重繼承?

Object-c的類沒有多繼承,只支持單繼承,如果要實現多繼承的話,可使用如下幾種方式間接實現

  • 通過組合實現

    A和B組合,作爲C類的組件

  • 通過協議實現

    C類實現A和B類的協議方法

  • 消息轉發實現

    forwardInvocation:方法

4.runtime 如何實現 weak 屬性?

weak 此特質表明該屬性定義了一種「非擁有關係」(nonowning relationship)。爲這種屬性設置新值時,設置方法既不持有新值(新指向的對象),也不釋放舊值(原來指向的對象)。

runtime 對註冊的類,會進行內存佈局,從一個粗粒度的概念上來講,這時候會有一個 hash 表,這是一個全局表,表中是用 weak 指向的對象內存地址作爲 key,用所有指向該對象的 weak 指針表作爲 value。當此對象的引用計數爲 0 的時候會 dealloc,假如該對象內存地址是 a,那麼就會以 a 爲 key,在這個 weak 表中搜索,找到所有以 a 爲鍵的 weak 對象,從而設置爲 nil。

runtime 如何實現 weak 屬性具體流程大致分爲 3 步:

  • 1、初始化時:runtime 會調用 objc_initWeak 函數,初始化一個新的 weak 指針指向對象的地址。

  • 2、添加引用時:objc_initWeak 函數會調用 objc_storeWeak() 函數,objc_storeWeak() 的作用是更新指針指向(指針可能原來指向着其他對象,這時候需要將該 weak 指針與舊對象解除綁定,會調用到 weak_unregister_no_lock),如果指針指向的新對象非空,則創建對應的弱引用表,將 weak 指針與新對象進行綁定,會調用到 weak_register_no_lock。在這個過程中,爲了防止多線程中競爭衝突,會有一些鎖的操作。

  • 3、釋放時:調用 clearDeallocating 函數,clearDeallocating 函數首先根據對象地址獲取所有 weak 指針地址的數組,然後遍歷這個數組把其中的數據設爲 nil,最後把這個 entry 從 weak 表中刪除,最後清理對象的記錄。

5.講一下 OC 的消息機制

  • OC中的方法調用其實都是轉成了objc_msgSend函數的調用,給receiver(方法調用者)發送了一條消息(selector方法名)

  • objc_msgSend底層有3大階段,消息發送(當前類、父類中查找)、動態方法解析、消息轉發

6.runtime具體應用

  • 利用關聯對象(AssociatedObject)給分類添加屬性

  • 遍歷類的所有成員變量(修改textfield的佔位文字顏色、字典轉模型、自動歸檔解檔)

  • 交換方法實現(交換系統的方法)

  • 利用消息轉發機制解決方法找不到的異常問題

  • KVC 字典轉模型

7.runtime如何通過selector找到對應的IMP地址?

每一個類對象中都一個對象方法列表(對象方法緩存)

  • 類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)。

  • 方法列表中每個方法結構體中記錄着方法的名稱,方法實現,以及參數類型,其實selector本質就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應的方法實現。

  • 當我們發送一個消息給一個NSObject對象時,這條消息會在對象的類對象方法列表裏查找。

  • 當我們發送一個消息給一個類時,這條消息會在類的Meta Class對象的方法列表裏查找。

8.簡述下Objective-C中調用方法的過程

Objective-C是動態語言,每個方法在運行時會被動態轉爲消息發送,即:objc_msgSend(receiver, selector),整個過程介紹如下:

  • objc在向一個對象發送消息時,runtime庫會根據對象的isa指針找到該對象實際所屬的類

  • 然後在該類中的方法列表以及其父類方法列表中尋找方法運行

  • 如果,在最頂層的父類(一般也就NSObject)中依然找不到相應的方法時,程序在運行時會掛掉並拋出異常unrecognized selector sent to XXX

  • 但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會,這三次拯救程序奔潰的說明見問題《什麼時候會報unrecognized selector的異常》中的說明。

9.load和initialize的區別

兩者都會自動調用父類的,不需要super操作,且僅會調用一次(不包括外部顯示調用).

  • load和initialize方法都會在實例化對象之前調用,以main函數爲分水嶺,前者在main函數之前調用,後者在之後調用。這兩個方法會被自動調用,不能手動調用它們。

  • load和initialize方法都不用顯示的調用父類的方法而是自動調用,即使子類沒有initialize方法也會調用父類的方法,而load方法則不會調用父類。

  • load方法通常用來進行Method Swizzle,initialize方法一般用於初始化全局變量或靜態變量。

  • load和initialize方法內部使用了鎖,因此它們是線程安全的。實現時要儘可能保持簡單,避免阻塞線程,不要再使用鎖。

10.怎麼理解Objective-C是動態運行時語言。

  • 主要是將數據類型的確定由編譯時,推遲到了運行時。這個問題其實淺涉及到兩個概念,運行時和多態。

  • 簡單來說, 運行時機制使我們直到運行時纔去決定一個對象的類別,以及調用該類別對象指定方法。

  • 多態:不同對象以自己的方式響應相同的消息的能力叫做多態。

  • 意思就是假設生物類(life)都擁有一個相同的方法-eat;那人類屬於生物,豬也屬於生物,都繼承了life後,實現各自的eat,但是調用是我們只需調用各自的eat方法。也就是不同的對象以自己的方式響應了相同的消 息(響應了eat這個選擇器)。因此也可以說,運行時機制是多態的基礎.

其他面試題篇章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章