iOS底層原理18:類的加載

在上一篇我們分析了_objc_init方法,程序運(yùn)行時(shí),dyld將使用包含objc_image_info的鏡像文件數(shù)組,回調(diào) mapped 函數(shù),最后會(huì)執(zhí)行libObjcmap_images方法

map_images的主要流程

map_images方法

  • map_images方法源碼如下,從注釋可以看出,這個(gè)方法主要用來處理映射到內(nèi)存中的鏡像文件
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

map_images_nolock方法

  • 查看map_images_nolock方法,里面代碼比較多,直接看重點(diǎn)
image

_read_images方法

  • 查看_read_images方法
image

_read_images內(nèi)容比較多,根據(jù)蘋果注釋信息,主要有以下幾步:

  • 條件控制進(jìn)行一次加載
  • 修復(fù)預(yù)編譯階段的@selector的混亂的問題
  • 錯(cuò)誤混亂的類處理
  • 修復(fù)重映射一些沒有被鏡像文件加載進(jìn)來的類
  • 修復(fù)一些消息
  • 當(dāng)類中有協(xié)議時(shí):readProtocol
  • 修復(fù)沒有被加載的協(xié)議
  • 分類的處理
  • 類的加載處理
  • 沒有被處理的類,優(yōu)化那些被侵犯的類

條件控制進(jìn)行一次加載

doneOnce是全局靜態(tài)變量,加載一次后doneOnce=YES,下次就不會(huì)在進(jìn)入判斷。第一次進(jìn)來主要?jiǎng)?chuàng)建表gdb_objc_realized_classes,表里存放的是不在dyld共享緩存中的命名類,無論是否實(shí)現(xiàn)

static bool doneOnce;
if (!doneOnce) {
    doneOnce = YES; // doneOnce:全局靜態(tài)變量,只加載一次
    launchTime = YES;
    
    // ...此處省略代碼
    // namedClasses 是不在dyld共享緩存中的命名類,無論是否實(shí)現(xiàn)。
    // Preoptimized classes don't go in this table.
    // 4/3 is NXMapTable's load factor 4/3是NXMapTable的負(fù)載系數(shù)
    int namedClassesSize = 
        (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
    // 創(chuàng)建哈希表 存放所有的類
    gdb_objc_realized_classes =
        NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);

    ts.log("IMAGE TIMES: first time tasks");
}

修復(fù)@selector的混亂

  • 修復(fù)@selector的混亂,從macho文件中獲取對象方法列表,方法列表存放在 DATA段__objc_selrefs
// Fix up @selector references
static size_t UnfixedSelectors;
{
    mutex_locker_t lock(selLock);
    for (EACH_HEADER) {
        if (hi->hasPreoptimizedSelectors()) continue;

        bool isBundle = hi->isBundle();
        // 從macho文件中獲取方法名列表,方法列表存放在 DATA段 的 __objc_selrefs
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
            const char *name = sel_cname(sels[i]);
            SEL sel = sel_registerNameNoLock(name, isBundle);
            if (sels[i] != sel) {
                // TODO:自定義打印
                _objc_inform("=======修復(fù)@selector的混亂:%p = %p", (SEL)sels[i], sel);
                sels[i] = sel;
            }
        }
    }
}
image

錯(cuò)誤混亂的類處理

// Discover classes. Fix up unresolved future classes. Mark bundle classes.
// 發(fā)現(xiàn)類。修復(fù)未解析的未來類。標(biāo)記捆綁類
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

for (EACH_HEADER) {
    if (! mustReadClasses(hi, hasDyldRoots)) {
        // Image is sufficiently optimized that we need not call readClass()
        continue;
    }
    
    // 從Mach-O中讀取類列表信息,類總數(shù)count
    classref_t const *classlist = _getObjc2ClassList(hi, &count);
    if (hIndex == hCount - 1) {
        _objc_inform("=======發(fā)現(xiàn)類。修復(fù)未解析的未來類。標(biāo)記捆綁類。:HTTest = %zu", count);
    }
    bool headerIsBundle = hi->isBundle();
    bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

    for (i = 0; i < count; i++) {
        Class cls = (Class)classlist[I];
        Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
        
        // 類信息發(fā)生混亂,類運(yùn)行時(shí)可能發(fā)生移動(dòng),但是沒有被刪除,相當(dāng)于常說的野指針
        if (newCls != cls  &&  newCls) {
            // Class was moved but not deleted. Currently this occurs 
            // only when the new class resolved a future class.
            // Non-lazily realize the class below.
            resolvedFutureClasses = (Class *)
                realloc(resolvedFutureClasses, 
                        (resolvedFutureClassCount+1) * sizeof(Class));
            resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
        }
    }
}
  • 自定義一個(gè)類HTPerson,設(shè)置相應(yīng)的斷點(diǎn)
image
  • 執(zhí)行readClass方法后,通過lldb查看cls的值,發(fā)現(xiàn)已經(jīng)跟類名關(guān)聯(lián)上了
image

??我們來探究readClass方法,看看他做了什么操作

readClass方法

這個(gè)方法主要是用來更新兩張表:類名稱表gdb_objc_realized_classes, 所有類的表allocatedClasses

image

通過斷點(diǎn)調(diào)試,我們發(fā)現(xiàn)readClass主要有三步操作:

  • 獲取類名mangledName
  • 將類名和地址關(guān)聯(lián)起來
  • 添加【類和元類】到 所有類的表allocatedClasses表)中,就是runtime_init中開辟的那個(gè)表
獲取類名
  • 查看nonlazyMangledName方法,內(nèi)部是通過bites屬性找到ro數(shù)據(jù),類名存放在class_ro_t結(jié)構(gòu)中
// Get the class's mangled name, or NULL if the class has a lazy
// name that hasn't been created yet.
const char *nonlazyMangledName() const {
    return bits.safe_ro()->getName();
}
  • 查看safe_ro方法,內(nèi)部就是獲取ro結(jié)構(gòu)體數(shù)據(jù),因?yàn)?code>class_ro_t結(jié)構(gòu)體中存儲(chǔ)了類名
const class_ro_t *safe_ro() const {
    class_rw_t *maybe_rw = data();
    if (maybe_rw->flags & RW_REALIZED) {
        // maybe_rw is rw
        // 已實(shí)現(xiàn)的類通過 bits -> rw -> ro來獲取
        return maybe_rw->ro();
    } else {
        // maybe_rw is actually ro
        // 未加載過的類bits屬性 存儲(chǔ)的就是 ro結(jié)構(gòu)體數(shù)據(jù)
        return (class_ro_t *)maybe_rw;
    }
}
image
image
addNamedClass將類名和地址關(guān)聯(lián)綁定起來
  • 查看addNamedClass方法,查看類名和地址是如何關(guān)聯(lián)起來
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
    runtimeLock.assertLocked();
    Class old;
    if ((old = getClassExceptSomeSwift(name))  &&  old != replacing) {
        inform_duplicate(name, old, cls);

        // getMaybeUnrealizedNonMetaClass uses name lookups.
        // Classes not found by name lookup must be in the
        // secondary meta->nonmeta table.
        addNonMetaClass(cls);
    } else {
        // 更新gdb_objc_realized_classes表,將key設(shè)置為 name value 設(shè)置為cls
        NXMapInsert(gdb_objc_realized_classes, name, cls);
    }
    ASSERT(!(cls->data()->flags & RO_META));

    // wrong: constructed classes are already realized when they get here
    // ASSERT(!cls->isRealized());
}
  • 更新gdb_objc_realized_classes哈希表,keyname,valuecls
添加【類和元類】到 所有類的表中
  • 查看addClassTableEntry方法,源碼如下,這一步就是將類和元類加入到allocatedClasses表中
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
    runtimeLock.assertLocked();

    // This class is allowed to be a known class via the shared cache or via
    // data segments, but it is not allowed to be in the dynamic table already.
    // _objc_init -> runtime_init 中初始化的表:所有類的表
    auto &set = objc::allocatedClasses.get();

    ASSERT(set.find(cls) == set.end());

    if (!isKnownClass(cls))
        set.insert(cls);
    if (addMeta)
        // 將元類插入哈希表中
        addClassTableEntry(cls->ISA(), false);
}

非懶加載類的加載

// +load方法的調(diào)用,是在 load_images方法中
// +load handled by prepare_load_methods()

// 加載非懶加載類
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
    // 從Mach-O中讀取非懶加載類列表信息,非懶加載類總數(shù)count
    classref_t const *classlist = hi->nlclslist(&count);
    if (hIndex == hCount - 1) {
        _objc_inform("=======加載非懶加載類:HTTest = %zu", count);
    }
    
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (!cls) continue;
        // 添加【非懶加載類和他的元類】到 所有類的表中
        addClassTableEntry(cls);

        if (cls->isSwiftStable()) {
            if (cls->swiftMetadataInitializer()) {
                _objc_fatal("Swift class %s with a metadata initializer "
                            "is not allowed to be non-lazy",
                            cls->nameForLogging());
            }
            // fixme also disallow relocatable classes
            // We can't disallow all Swift classes because of
            // classes like Swift.__EmptyArrayStorage
        }
        
        realizeClassWithoutSwift(cls, nil);
    }
}
  • 非懶加載類:實(shí)現(xiàn)了+ (void)load方法的類,程序啟動(dòng)時(shí)會(huì)加載
  • 懶加載類:沒有實(shí)現(xiàn)+ (void)load方法的類,在類首次使用時(shí)才會(huì)加載
  • 可以查看可執(zhí)行文件Mach-O,在DATA段__objc_nlclslist中存放的是非懶加載類

現(xiàn)在有兩個(gè)類:HTPersonHTTeacher,其中HTPerson類實(shí)現(xiàn)了+ (void)load方法,運(yùn)行程序,通過MachOView查看可執(zhí)行文件

image

image

  • 設(shè)置相應(yīng)的斷點(diǎn),通過lldb打印cls,發(fā)現(xiàn)此時(shí)只有一個(gè)非懶加載類,即HTPerson類,地址為0x00000001000082e8,與Mach-O的非懶加載類表相對應(yīng)
image
  • 接下來我們繼續(xù)分析最重要的一個(gè)方法,realizeClassWithoutSwift
    image

類的加載

realizeClassWithoutSwift方法分析

realizeClassWithoutSwift對類cls執(zhí)行首次初始化,包括分配其讀寫數(shù)據(jù)(即 rw數(shù)據(jù),用于運(yùn)行時(shí)記錄類的信息),返回類的實(shí)際類結(jié)構(gòu)

  • 非懶加載類程序啟動(dòng)時(shí),就會(huì)執(zhí)行realizeClassWithoutSwift方法
  • 懶加載類在使用時(shí)才會(huì)去加載,我們在方法慢速查找時(shí)有看到過,執(zhí)行流程:lookUpImpOrForward --> realizeAndInitializeIfNeeded_locked --> realizeClassMaybeSwiftAndLeaveLocked --> realizeClassMaybeSwiftMaybeRelock --> realizeClassWithoutSwift
image
  • realizeClassWithoutSwift方法源碼如下,代碼比較多,源碼如下
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    class_rw_t *rw;
    Class supercls;
    Class metacls;

    if (!cls) return nil;
    // 如果類已經(jīng)實(shí)現(xiàn),直接返回
    if (cls->isRealized()) {
        validateAlreadyRealizedClass(cls);
        return cls;
    }
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?
    // 獲取ro數(shù)據(jù),類未初始化時(shí),類結(jié)構(gòu)objc_class的 bits屬性存儲(chǔ)的其實(shí)是 ro數(shù)據(jù)
    auto ro = (const class_ro_t *)cls->data();
    auto isMeta = ro->flags & RO_META;
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        // 開辟可讀寫數(shù)據(jù),即rw,bits屬性此時(shí)存儲(chǔ)的是 rw數(shù)據(jù)
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
    }

    cls->cache.initializeToEmptyOrPreoptimizedInDisguise();

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    // 遞歸實(shí)現(xiàn) 父類和元類
    supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) {
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->getName(), "OS_object"))
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->getSuperclass()  &&
                 supercls->instancesRequireRawIsa())
        {
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        if (instancesRequireRawIsa) {
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    // 設(shè)置superclass 和 isa
    cls->setSuperclass(supercls);
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    // 設(shè)置fastInstanceSize,編譯器快速計(jì)算對象內(nèi)存大小
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    // 建立類 子類的雙向鏈表關(guān)系
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    // 附加類別 - 方法化當(dāng)前的類,方法排序,對類進(jìn)行擴(kuò)展
    methodizeClass(cls, previously);

    return cls;
}

realizeClassWithoutSwift主要進(jìn)行了下列幾步操作:
1、rw初始化,這里涉及到干凈內(nèi)存clean memory臟內(nèi)存dirty memory的概念。

  • ro屬于clean memory,在編譯時(shí)即確定的內(nèi)存空間,只讀,加載后不會(huì)發(fā)生改變的內(nèi)存空間,包括類名稱、方法、協(xié)議和實(shí)例變量的信息;
  • rw的數(shù)據(jù)空間屬于dirty memory,rw是運(yùn)行時(shí)的結(jié)構(gòu),可讀可寫,由于其動(dòng)態(tài)性,可以往類中添加屬性、方法、協(xié)議。在運(yùn)行時(shí)會(huì)發(fā)生變更的內(nèi)存
  • rwe類的額外信息。在WWDC2020中也提到,只有不到10%的類真正的更改了他們的方法,并不是每一個(gè)類都需要插入數(shù)據(jù),進(jìn)行修改的類很少,避免資源的消耗,所以就有了rwe。
image
image

2、遞歸處理,進(jìn)行父類和元類的實(shí)現(xiàn)。


image

3、isa處理,在前面學(xué)習(xí)isa的時(shí)候,對于NONPOINTER_ISA進(jìn)行了位域處理,指針優(yōu)化,isa的末尾位是1isa不單單代表一個(gè)指針。而對于元類以及特殊情況下的場景的一些類,無需開啟指針優(yōu)化的類,使用Raw Isaisa的末尾位是0。

image

4、設(shè)置superclassisa屬性,用來獲取父類和元類

image

5、設(shè)置fastInstanceSize,編譯器快速計(jì)算對象內(nèi)存大小

image

6、c++析構(gòu)函數(shù)的相關(guān)設(shè)置,以及關(guān)聯(lián)對象的相關(guān)設(shè)置。

image

7、建立子類與父類的雙定鏈表關(guān)系,保證子類能找到父類,父類也可以找到子類。

image

8、方法化當(dāng)前的類,向類中添加方法,協(xié)議、屬性,同時(shí)對方法列表進(jìn)行排序等操作。


image

methodizeClass方法分析

methodizeClass方法源碼如下:

static void methodizeClass(Class cls, Class previously)
{
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro();
    auto rwe = rw->ext();

    // Methodizing for the first time
    if (PrintConnecting) {
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
        rwe->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
        // root metaclass
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    if (previously) {
        if (isMeta) {
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods()) {
        if (PrintConnecting) {
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name()));
        }
        ASSERT(sel_registerName(sel_getName(meth.name())) == meth.name());
    }
#endif
}
  • 1、對方法、屬性、協(xié)議進(jìn)行處理。見下圖:

    image

    從上圖可以發(fā)現(xiàn):rwe為空,很顯然,此時(shí)還沒有對類進(jìn)行相關(guān)的擴(kuò)展操作,所以rwe還沒有被創(chuàng)建初始化。此時(shí)針對方法、屬性、協(xié)議的添加操作時(shí)無效的!

  • 2、方法列表的處理中有些不同,調(diào)用了prepareMethodLists方法。那么該方法做了哪些操作呢?見下圖:

image

核心流程,fixupMethodList,根據(jù)注釋:根據(jù)需要對selector進(jìn)行修復(fù)。進(jìn)入fixupMethodList方法,查看實(shí)現(xiàn)流程。見下圖:

image

  • 繼續(xù)methodizeClass源碼的解讀。找到了類初始化過程中非常關(guān)鍵的步驟,向類中添加分類方法、協(xié)議等,rwe的初始化也在其中。
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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