2021- iOS 面試題分析(一) 目錄 2. iOS面試題分析

目錄

1.回顧
1.1 補充

  1. iOS面試題分析
    2.1 load與c++構造函數調用順序
    2.2 runtime是什麼?
    2.3 initialize調用順序
    2.4 同名分類方法的調用順序
    2.5 分類和擴展的區別?

1.回顧

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


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的時候

* dealloc
// Replaced by NSZombies
- (void)dealloc {
    _objc_rootDealloc(self);
}
  • _objc_rootDealloc
void
_objc_rootDealloc(id obj)
{
    ASSERT(obj);

    obj->rootDealloc();
}

對象釋放就會去找rootDealloc

  • 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_dispose。

  • 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也就是最開頭的那個移除關聯對象方法。

所以,關聯對象不需要我們手動移除,會在dealloc時自動進行釋放

2. iOS面試題分析

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

  • load是在dyld回調load_images中進行調用的,這個回調是在_objc_init的過程中進行註冊的。
  • C++構造函數對於同一個image而言是在load回調後dyld調用的。(並不是絕對的)
  • image內部是先加載所有類的+ load,再加載分類的+ load,最後加載C++全局構造函數。(類load -> 分類load -> C++構造函數)。
  • +load是objc中調用的,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是由C 和C++/ 彙編 實現的⼀套API,爲OC語⾔加⼊了⾯向對象,運⾏時的功能。
  • 是一種運行機制,不是底層。dyld、彙編、objc、macho纔是底層。
  • 平時編寫的OC代碼,在程序運行過程中,其實最終會轉換成Runtime的C語言代 碼,Runtime 是 Objective-C的幕後工作者。

2.3 initialize調用順序

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

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

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

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

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

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

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

2.5 分類和擴展的區別?

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

category: 類別/分類

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

extension:類擴展

  • 可以說成是特殊的分類,也稱作匿名分類
  • 可以給類添加成員屬性,但是是私有變量
  • 可以給類添加方法,也是私有方法
  • 分類我們已經很熟悉了,這裏就不必過多贅述了,下面介紹下擴展

extension

類擴展,我們平時用的是非常多的,如下

what ? 什麼,這就是擴展嗎?天天用居然不知道!
是的,這就是擴展,平時用的是非常之多,但是很多人都不知道。


注意:類擴展要放在聲明之後,實現之前,否則會報錯。

更多內容持續更新

🌹 喜歡就點個贊吧👍🌹

🌹 覺得有收穫的,可以來一波,收藏+關注,評論 + 轉發,以免你下次找不到我😁🌹

🌹歡迎大家留言交流,批評指正,互相學習😁,提升自我🌹

如果你正在跳槽或者正準備跳槽不妨動動小手,添加一下咱們的交流羣1012951431來獲取一份詳細的大廠面試資料爲你的跳槽多添一份保障。

文末推薦:iOS熱門文集

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