iOS底層原理之部分面試題分析

Runtime Asssociate方法關聯的對象,是否須要在dealloc中釋放?

不須要釋放markdown

分析

咱們知道當一個對象銷燬的時候會調用dealloc方法,那麼咱們先看下dealloc都進行了哪些操做。ide

  • dealloc函數調用了_objc_rootDealloc函數

  • _objc_rootDealloc函數調用rootDealloc函數
void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);

    obj->rootDealloc();
}
複製代碼
  • rootDealloc函數查看
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}
複製代碼

rootDealloc函數中咱們看到了判斷isa相關屬性的地方,實際上當一個對象存在會進入else中,即object_dispose函數函數

  • object_dispose函數查看
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
複製代碼

經過objc_destructInstance函數找到對象,而後free,咱們看objc_destructInstance函數this

  • objc_destructInstance函數

重點查看_object_remove_assocations函數spa

  • _object_remove_assocations函數分析

類、分類方法同名時調用順序是怎樣的?

非+load方法同名時,分類的方法在類的方法前面(注意不是覆蓋),由於分類的方法是在類realize以後 attach進去的,因此 優先分類,其次類

+load方法同名時,優先類,其次分類

分類與類的擴展

分類

  • 專門用來給類添加新的方法
  • 不能添加屬性,可是能夠經過runtime動態添加屬性(由於咱們在前面的篇章中分析過,分類底層代碼中有屬性列表)
  • 分類中@property定義的變量只會生成setter以及getter方法的聲明,可是不會生成對應的方法實現以及帶有下劃線的成員變量

類的擴展

  • 能夠添加給類添加屬性,可是屬於私有變量,好比ViewController的.m文件中@property定義的變量只能是這個ViewController的.m文件使用
  • 添加的方法也是私有方法

什麼是Runtime?

runtime是由C和C++彙編實現的一套API,爲OC語言添加了面向對象和運行時功能。3d

  • 運行時:將數據類型的肯定由編譯階段推遲到了運行階段。咱們平時所寫的OC代碼,最終轉換爲runtime的C語言代碼。

方法的本質是什麼?SEL、IMP是什麼?二者之間的關係是什麼?

方法的本質

方法的本質是消息的發送,涉及到消息發送的流程有指針

  • 快速查找:objc_msgSend ~cache_t中查找
  • 慢速查找:遞歸本身以及父類查找,即lookUpImpOrForward
  • 動態解析:當查找不到消息時執行動態解析,即resolveInstanceMethod
  • 消息快速轉發:當動態解析也沒有找到消息,則進行消息快速轉發,即forwardingTargetForSelector
  • 消息慢速轉發:消息快速轉發沒有找到消息,則進行消息慢速轉發,即mesthodSignatureForSelector & forwardInvocation
  • 以上流程均沒有找到消息則crash

SEL、IMP

  • sel:方法編號,類比一本書的目錄
  • imp:方法函數指針地址,類比一本書的頁數
  • sel與imp關係:sel是方法編號,經過sel找到imp的函數指針地址,經過imp就能找到函數的實現

可否向編譯後的類中添加實例變量?可否向運行時建立的類添加實例變量?

  • 不能向編譯後的類中添加實例變量,由於編譯後實例變量存儲到 ro 中,一旦編譯完成,內存結構就徹底肯定了,沒法再次修改
  • 只要類尚未註冊到內存仍是能夠添加的
  • 能夠添加屬性與方法

[self class] 與 [super class]的區別

咱們先看如下以下代碼打印結果,其中self是LGTeacher類,LGTeacher繼承於LGPerson,LGPerson繼承於NSObject從打印結果中咱們看到不管是[self class]仍是[super class]的結果是同樣的,爲何呢?code

分析

  • 咱們知道任何方法調用都會隱藏兩個參數,即(id self , sel _cmd),其中self是消息接收者。對於[self class]來講,它的消息接收者是 自身LGTeacher沒什麼可說的,因此打印的是LGTeacher
  • 首先咱們要知道super只是關鍵字,它意思是說從父類調用方法,所以[super class]就是直接調用的就是父類的class方法,它的本質是objc_msgSendSuper,只是objc_msgSendSuper速度更快,直接跳過self。但須要注意的是,[super class]的消息接受者依然是LGTeacher,因此最終打印的是LGTeacher

內存偏移相關問題

咱們先準備代碼,定義IFPerson類,代碼以下咱們再看ViewController代碼 從上述代碼中咱們延伸出兩個問題:代碼是否崩潰doSomething打印結果是什麼。先不回答這兩個問題,咱們運行代碼看結果如何,運行結果以下圖 從運行結果中咱們能夠看出代碼不會崩潰且運行結果也出來了.orm

[(__bridge id)kc doSomething]爲何不會崩潰?

首先咱們知道對於一個對象,它的指針地址指向的是isa,同時isa地址指向當前的class,因此kc指向的是IFPersonisa,而person的指針指向的也是isa,這樣它們都是isacache_t中查找doSomething方法,所以不會崩潰。對象

爲何[(__bridge id)kc doSomething]打印的結果是ViewController

  • 從打印結果中[person doSomething]打印出出來shifx是沒有什麼問題的,畢竟給person.name賦值shifx,可是[(__bridge id)kc doSomething]打印的結果是ViewController呢?要解決這個問題首先咱們須要知道person可以找到name指針從isa內存平移了8個字節移動到了name。那麼對於kc來講,它也須要指針平移,可是爲何平移後的結果是viewController呢?這就須要明白棧地址是從高到低存儲的,且是先進後出,因爲前面先調用了[super viewDidLoad]方法,且viewDidLoad的隱藏參數是(id self, SEL _cmd),因此self會先入棧,其次是cls->kc->person,出棧的順序恰好相反,因爲[(__bridge id)kc doSomething]時須要指針平移,天然指向了self(即ViewController),因此打印的結果是ViewController
  • 爲了驗證咱們上面分析是否正確,咱們修改代碼位置,將聲明IFPerson *person = [[IFPerson alloc] init]放在[super viewDidLoad]以後,即

此時咱們按照咱們上面的分析self會先入棧,其次是person->cls->kc,猜想[(__bridge id)kc doSomething]打印結果應該是IFPerson (person的isa指向其Class),咱們運行代碼結果能夠看出咱們的分析是正確的。

相關文章
相關標籤/搜索