iOS 面試題分析(一)|8月更文挑戰

1.回顧

在以前的博客中,對OC底層進行了一系列的探索分析,相信小夥伴們都學到了必定的知識,可是底層源碼分析比較枯燥,那麼本次就對一些面試題進行分析。 iOSc++

1.1 補充

在上篇博客iOS底層探索之類的加載(四):類的關聯對象AssociatedObject中主要講了類的擴展類的關聯對象移除關聯尚未講,這裏就作一點補充。面試

  • 移除關聯對象objc_removeAssociatedObjects
void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object, /*deallocating*/false);
    }
}
複製代碼
  • _object_remove_assocations
void
_object_remove_assocations(id object, bool deallocating)
{
    ObjectAssociationMap refs{};

    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        if (i != associations.end()) {
            refs.swap(i->second);

            // If we are not deallocating, then SYSTEM_OBJECT associations are preserved.
            bool didReInsert = false;
            if (!deallocating) {
                for (auto &ref: refs) {
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
                        i->second.insert(ref);
                        didReInsert = true;
                    }
                }
            }
            if (!didReInsert)
                associations.erase(i);
        }
    }

    // Associations to be released after the normal ones.
    SmallVector<ObjcAssociation *, 4> laterRefs;

    // release everything (outside of the lock).
    for (auto &i: refs) {
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // If we are not deallocating, then RELEASE_LATER associations don't get released.
            if (deallocating)
                laterRefs.append(&i.second);
        } else {
            i.second.releaseHeldValue();
        }
    }
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}

複製代碼

上面👆這是移除關聯對象的代碼,這裏就不過多分析源碼了,咱們看看在哪裏調用了數組

在對象的生命週期,dealloc的時候markdown

  • dealloc
// Replaced by NSZombies
- (void)dealloc {
    _objc_rootDealloc(self);
}
複製代碼
  • _objc_rootDealloc
void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);

    obj->rootDealloc();
}
複製代碼

對象釋放就會去找rootDeallocapp

  • rootDealloc
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer                     &&
                 !isa.weakly_referenced             &&
                 !isa.has_assoc                     &&
#if ISA_HAS_CXX_DTOR_BIT
                 !isa.has_cxx_dtor                  &&
#else
                 !isa.getClass(false)->hasCxxDtor() &&
#endif
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}
複製代碼

這裏會判斷isa.nonpointer、弱引用isa.weakly_referenced、關聯對象isa.has_assoc等等,有的話就進入object_disposeide

  • object_dispose
id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}

複製代碼

進入objc_destructInstance銷燬實例函數

  • objc_destructInstance
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj, /*deallocating*/true);
        obj->clearDeallocating();
    }

    return obj;
}
複製代碼

C++方法,關聯對象方法判斷,就去走_object_remove_assocations也就是最開頭的那個移除關聯對象方法。oop

因此,關聯對象不須要咱們手動移除,會在dealloc時自動進行釋放源碼分析

2. iOS面試題分析

2.1 load與c++構造函數調用順序

  • load是在dyld回調load_images中進行調用的,這個回調是在_objc_init的過程當中進行註冊的。
  • C++構造函數對於同一個image而言是在load回調後dyld調用的。(並非絕對的)
  • image內部是先加載全部類的+ load,再加載分類的+ load,最後加載C++全局構造函數。(類load -> 分類load -> C++構造函數)。
  • +loadobjc中調用的,C++全局構造函數是在dyld中調用的。(image內部的順序默認是按Compile Sources中順序進行加載的,固然對於有依賴庫的image,依賴庫+load先被調用)。
  • Dyld初始化image是按Link Binary With Libraries順序逐個初始化的,從下標1開始,最後再初始化主程序(下標0)。
  • 固然對於同一個image而言C++構造函數在load以後調用並非絕對的。好比objc系統庫,在進行dyld註冊回調以前調用了自身庫的C++構造函數(自啓)。

2.2 runtime是什麼?

  • runtime 是由CC++/ 彙編 實現的⼀套API,爲OC語⾔加⼊了⾯向對象,運⾏時的功能。
  • 是一種運行機制,不是底層。dyld彙編objcmacho纔是底層。
  • 平時編寫的OC代碼,在程序運行過程當中,其實最終會轉換成RuntimeC語言代 碼,RuntimeObjective-C 的幕後工做者。

2.3 initialize調用順序

  • initialize是在第一次發送消息的時候進行的調用。學習

  • load是在load_images的時候調用的,loadinitialize調用時機早(initializelookupimporforward慢速消息查找的時候調用)。

  • 整個調用順序load > C++構造函數 > initialize

2.4 同名分類方法的調用順序

同名分類方法調用順序分爲兩種狀況:

  • 分類合併到主類的狀況,也就是隻有一個/或者沒有load方法,這個時候整個方法列表一維數組(不考慮其它動態添加方法的狀況)。最後編譯的同名分類方法會放在主類與其它分類前面,在進行方法二分查找的時候先從中間開始查找,找到對應方法SEL的時候會繼續往前查找,直到找到最前面的同名方法。
  • 分類沒有合併到主類的狀況,多個load方法這個時候整個方法列表是一個一個二維數組,後編譯加載的分類在數組前面,查找方法的時候從前面開始查找。
  • 也就是同名方法最終會找到後編譯加載的分類的同名方法,查找過程不同而已。

iOS底層探索之類的加載(三): attachCategories分析博客裏面也介紹了分類和load方法的一些加載。

2.5 分類和擴展的區別?

首先咱們來看看什麼是分類和擴展

category: 類別/分類

  • 專門用來給類添加新的方法
  • 不能給類添加成員屬性,添加了成員變量,也沒法取到
  • 注意:其實能夠經過runtime給分類添加屬性
  • 分類中用@property定義變量,只會生成變量的 getter,setter方法的聲明,不能生成方法實現和帶下劃線的成員變量

extension:類擴展

  • 能夠說成是特殊的分類,也稱做匿名分類
  • 能夠給類添加成員屬性,可是是私有變量
  • 能夠給類添加方法,也是私有方法

分類咱們已經很熟悉了,這裏就沒必要過多贅述了,下面介紹下擴展

extension

類擴展,咱們平時用的是很是多的,以下

匿名擴展

what ? 什麼,這就是擴展嗎?每天用竟然不知道!

是的,這就是擴展,平時用的是很是之多,可是不少人都不知道。

震驚

注意:類擴展要放在聲明以後,實現以前,不然會報錯。

更多內容持續更新

🌹 喜歡就點個贊吧👍🌹

🌹 以爲有收穫的,能夠來一波,收藏+關注,評論 + 轉發,以避免你下次找不到我😁🌹

🌹歡迎你們留言交流,批評指正,互相學習😁,提高自我🌹

相關文章
相關標籤/搜索