美文网首页
iOS 通过Category的加载来看类的加载

iOS 通过Category的加载来看类的加载

作者: forping | 来源:发表于2021-02-24 17:31 被阅读0次

    首先,在编译的时候,分类会被编译成 静态category_t 结构体变量,之后在运行的时候加载,和类对象关联起来

    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    

    在编译的时候,实例对象对应的结构体构建完毕(包含了成员变量). 分类在运行时加载,不能再扩充成员变量.

    运行时加载.

    Objc setup主要是在objc_init完成的,objc_init是在libsystem中的一个initialize方法libsystem_initializer中初始化了libdispatch,然后libdispatch_init调用了_os_object_init, 最终调用了_objc_init

    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // fixme defer initialization until an objc-using image is found?
    // 运行时环境的初始化
        environ_init();
    // 关于线程key的绑定-比如每条线程数据的析构函数。
        tls_init();
    // 运行C++静态构造函数。在dyld调用我们的静态构造函数之前,libc会调用_objc_init()
        static_init();
    // runtime运行时环境初始化,里面主要是:unattachedCategories,allocatedClasses
        runtime_init();
    // 初始化libobjc的异常处理系统
        exception_init();
    // 缓存条件初始化
        cache_init();
    //_imp_implementationWithBlock_init():启动回调机制。通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,会加载trampolines。
        _imp_implementationWithBlock_init();
    //   runtime在_objc_init向dyld绑定了3个回调函数,分别是map_images,load_images和unmap_image
    //  在map_images 中我们加载类和分类
    //  在load_images 中关联类和分类,并调用类和分类的load方法
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }
    

    map_images

    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);
    }
    

    dyld相关方法

    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped)
    {
        dyld::registerObjCNotifiers(mapped, init, unmapped);
    }
    

    registerObjCNotifiers

    void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
    {
        // record functions to call
        sNotifyObjCMapped   = mapped;
        sNotifyObjCInit     = init;
        sNotifyObjCUnmapped = unmapped;
    
        // call 'mapped' function with all images mapped so far
        try {
            notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
        }
        catch (const char* msg) {
            // ignore request to abort during registration
        }
    
        // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
        for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
            ImageLoader* image = *it;
            if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
                (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
    
            }
        }
    }
    

    sNotifyObjCMapped:map_images 的调用

    static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
    {
        ···
        if ( objcImageCount != 0 ) {
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
            uint64_t t0 = mach_absolute_time();
            (*sNotifyObjCMapped)(objcImageCount, paths, mhs);
            uint64_t t1 = mach_absolute_time();
            ImageLoader::fgTotalObjCSetupTime += (t1-t0);
        }
        ···
    }
    

    sNotifyObjCInit : load_images 的调用

    static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
    {
        ···
        if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
            uint64_t t0 = mach_absolute_time();
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
    // sNotifyObjCInit : load_images 的调用
            (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
            uint64_t t1 = mach_absolute_time();
            uint64_t t2 = mach_absolute_time();
            uint64_t timeInObjC = t1-t0;
            uint64_t emptyTime = (t2-t1)*100;
            if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
                timingInfo->addTime(image->getShortName(), timeInObjC);
            }
        }
        ···
    }
    
    

    类和分类的加载

    map_images_nolock

    map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[]){
    ···
          if (hCount > 0) {
              _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
          }
    ···
    }
    

    _read_images

    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
    {
    ···
        // Fix up @selector references 修复方法选择器
        static size_t UnfixedSelectors;
        {
            mutex_locker_t lock(selLock);
            for (EACH_HEADER) {
                if (hi->hasPreoptimizedSelectors()) continue;
    
                bool isBundle = hi->isBundle();
    // 获取方法选择器
                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) {
                        sels[i] = sel;
                    }
                }
            }
        }
    
        ts.log("IMAGE TIMES: fix up selector references");
    
        // Discover classes. Fix up unresolved future classes. Mark bundle classes. 
        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获取类列表
            classref_t const *classlist = _getObjc2ClassList(hi, &count);
              
            bool headerIsBundle = hi->isBundle();
            bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
    
            for (i = 0; i < count; i++) {
                Class cls = (Class)classlist[i];
    // 经过readClass方法后cls绑定了类名。添加到类对象的集合里
                Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    
                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;
                }
            }
        }
        ts.log("IMAGE TIMES: discover classes");
    
    ···
    
        // Discover protocols. Fix up protocol refs.
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
    // 当我们类里面有协议的时候:添加协议
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            ASSERT(cls);
            NXMapTable *protocol_map = protocols();
            bool isPreoptimized = hi->hasPreoptimizedProtocols();
    
            if (launchTime && isPreoptimized) {
                if (PrintProtocols) {
                    _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
                                 hi->fname());
                }
                continue;
            }
    
            bool isBundle = hi->isBundle();
    
            protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                readProtocol(protolist[i], cls, protocol_map, 
                             isPreoptimized, isBundle);
            }
        }
    
        ts.log("IMAGE TIMES: discover protocols");
    
    // 修复没有被加载的协议
        // Fix up @protocol references
        // Preoptimized images may have the right 
        // answer already but we don't know for sure.
        for (EACH_HEADER) {
            if (launchTime && hi->isPreoptimized())
                continue;
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[i]);
            }
        }
    
        ts.log("IMAGE TIMES: fix up @protocol references");
    
    // 处理分类
        if (didInitialAttachCategories) {
            for (EACH_HEADER) {
                load_categories_nolock(hi);
            }
        }
    
        ts.log("IMAGE TIMES: discover categories");
    
        // 类的加载处理:实现非懒加载类(实现了+load方法的类和静态实例)
        for (EACH_HEADER) {
            classref_t const *classlist = hi->nlclslist(&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
                }
    // 重新初始化类,绑定rw 信息 并且递归调用父类
                realizeClassWithoutSwift(cls, nil);
            }
        }
    
        ts.log("IMAGE TIMES: realize non-lazy classes");
    // 没有被处理的类 优化被侵入的类
        if (resolvedFutureClasses) {
            for (i = 0; i < resolvedFutureClassCount; i++) {
                Class cls = resolvedFutureClasses[i];
                if (cls->isSwiftStable()) {
                    _objc_fatal("Swift class is not allowed to be future");
                }
                realizeClassWithoutSwift(cls, nil);
                cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
            }
            free(resolvedFutureClasses);
        }
    
        ts.log("IMAGE TIMES: realize future classes");
    
        if (DebugNonFragileIvars) {
            realizeAllClasses();
        }
    }
    

    非懒加载类:实现了+load方法(会提前加载,load_images方法中会调用所有+load方法)
    懒加载类: 未实现+load方法,懒加载类的加载时机在于类的第一次消息发送

    realizeClassWithoutSwift

    static Class realizeClassWithoutSwift(Class cls, Class previously)
    {
        runtimeLock.assertLocked();
    
        class_rw_t *rw;
        Class supercls;
        Class metacls;
    
        if (!cls) return nil;
        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?
    
        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.
            rw = objc::zalloc<class_rw_t>();
            rw->set_ro(ro);
            rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
            cls->setData(rw);
        }
    ···
    // 递归调用realizeClassWithoutSwift方法实现父类及元类
        supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
    ···
    
            if (instancesRequireRawIsa) {
    // 将这个类及其所有子类标记为需要原始isa指针
                cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
            }
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
    // 设置数据
        // Update superclass and metaclass in case of remapping
        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.
        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
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
        // Attach categories 加载 分类 
        methodizeClass(cls, previously);
    
        return cls;
    }
    

    methodizeClass 修复cls的方法列表、协议列表和属性列表。

    static void methodizeClass(Class cls, Class previously)
    {
        ···
    // 基础方法
        method_list_t *list = ro->baseMethods();
        if (list) {
    // 方法排序
            prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
    // 添加到rwe
            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);
        }
    // 加载分类
        objc::unattachedCategories.attachToClass(cls, cls,
                                                 isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
    }
    

    方法排序

    prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
                       bool baseMethods, bool methodsFromBundle)
    {
    ...
            // Fixup selectors if necessary
            if (!mlist->isFixedUp()) {
                fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
            }
    ...
    }
    
    static void 
    fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
    {
    // 类中的方法列表是通过方法地址SEL排序的
    // 小列表和非标准大小的大列表不排序
        if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
            method_t::SortBySELAddress sorter;
            std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
        }
    }
    

    虽然methodizeClass函数中调用了attachToClass函数.但调用流程里并没有走attachCategories函数

    通过打断点,发现在 load_images方法中

    void
    load_images(const char *path __unused, const struct mach_header *mh)
    {
        if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
            didInitialAttachCategories = true;
    // 加载分类
            loadAllCategories();
        }
    
        // Return without taking locks if there are no +load methods here.
        if (!hasLoadMethods((const headerType *)mh)) return;
    
        recursive_mutex_locker_t lock(loadMethodLock);
    
        // Discover load methods
        {
            mutex_locker_t lock2(runtimeLock);
            prepare_load_methods((const headerType *)mh);
        }
    
        // Call +load methods (without runtimeLock - re-entrant)
    // 调用load方法
        call_load_methods();
    }
    

    loadAllCategories

    static void loadAllCategories() {
        mutex_locker_t lock(runtimeLock);
    
        for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
            load_categories_nolock(hi);
        }
    }
    

    load_categories_nolock

    static void load_categories_nolock(header_info *hi) {
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
        size_t count;
        auto processCatlist = [&](category_t * const *catlist) {
            for (unsigned i = 0; i < count; i++) {
                category_t *cat = catlist[i];
                Class cls = remapClass(cat->cls);
    // 封装分类
                locstamped_category_t lc{cat, hi};
    
                // Process this category.
                if (cls->isStubClass()) {
                  
                    if (cat->instanceMethods ||
                        cat->protocols ||
                        cat->instanceProperties ||
                        cat->classMethods ||
                        cat->protocols ||
                        (hasClassProperties && cat->_classProperties))
                    {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                } else {
                    // First, register the category with its target class.
                    // Then, rebuild the class's method lists (etc) if
                    // the class is realized.
                    if (cat->instanceMethods ||  cat->protocols
                        ||  cat->instanceProperties)
                    {
                        if (cls->isRealized()) {
    // 附加分类
                            attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls);
                        }
                    }
    
                    if (cat->classMethods  ||  cat->protocols
                        ||  (hasClassProperties && cat->_classProperties))
                    {
                        if (cls->ISA()->isRealized()) {
                            attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls->ISA());
                        }
                    }
                }
            }
        };
    
        processCatlist(hi->catlist(&count));
        processCatlist(hi->catlist2(&count));
    }
    
    static void
    attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                     int flags)
    {
        
        constexpr uint32_t ATTACH_BUFSIZ = 64;
        method_list_t   *mlists[ATTACH_BUFSIZ];
        property_list_t *proplists[ATTACH_BUFSIZ];
        protocol_list_t *protolists[ATTACH_BUFSIZ];
    
        uint32_t mcount = 0;
        uint32_t propcount = 0;
        uint32_t protocount = 0;
        bool fromBundle = NO;
        bool isMeta = (flags & ATTACH_METACLASS);
    // 初始化rwe
        auto rwe = cls->data()->extAllocIfNeeded();
    
        for (uint32_t i = 0; i < cats_count; i++) {
            auto& entry = cats_list[i];
             // 获得方法列表
            method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
            if (mlist) {
                if (mcount == ATTACH_BUFSIZ) { // 如果数组存满了,就先添加方法列表
    // 方法排序
                    prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
    // 添加方法
                    rwe->methods.attachLists(mlists, mcount);
                    mcount = 0;
                }
                mlists[ATTACH_BUFSIZ - ++mcount] = mlist; // 存放在数组的最后
                fromBundle |= entry.hi->isBundle();
            }
    // 属性
            property_list_t *proplist =
                entry.cat->propertiesForMeta(isMeta, entry.hi);
            if (proplist) {
                if (propcount == ATTACH_BUFSIZ) {
                    rwe->properties.attachLists(proplists, propcount);
                    propcount = 0;
                }
                proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
            }
    
    // 协议
            protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
            if (protolist) {
                if (protocount == ATTACH_BUFSIZ) {
                    rwe->protocols.attachLists(protolists, protocount);
                    protocount = 0;
                }
                protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
            }
        }
    
    // 把数组的数据存放到 类信息里
        if (mcount > 0) {
            prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                               NO, fromBundle, __func__);
            rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
            if (flags & ATTACH_EXISTING) {
                flushCaches(cls, __func__, [](Class c){
                    // constant caches have been dealt with in prepareMethodLists
                    // if the class still is constant here, it's fine to keep
                    return !c->cache.isConstantOptimizedCache();
                });
            }
        }
    
        rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
    
        rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
    }
    

    attachCategories方法中通过调用extAllocIfNeeded()生成了rwe,因此调用extAllocIfNeeded()的地方就会生成了rwe。全局搜索extAllocIfNeeded ()即可发现attachCategoriesaddMethodclass_addProtocol_class_addProperty等地方调用了extAllocIfNeeded()

    如果类在有分类,动态添加方法、协议、属性的情况下才会生成rwe。

    懒加载与非懒加载下分类的加载情况

    1. 主类为非懒加载类(+load)、分类一为非懒加载分类(+load)、分类二为懒加载分类 只要分类中有一个为非懒加载分类那么所有分类均会标记为非懒加载分类load_images方法开始获取rwe数据

    2. 主类为懒加载类、分类均为懒加载分类 第一次消息发送的时候加载类信息,rwe数据从machoclsdata()获取

    3. 主类为非懒加载类(+load)、分类均为懒加载分类 rwe数据从machoclsdata()获取

    4. 主类为懒加载类、分类均为非懒加载分类(+load) 分类迫使主类变为非懒加载类样式来提前加载数据

    class_ro_t

    在类的加载过程中可以直接从macho中读取内存地址并能获取class_ro_t格式的数据,那是什么时候通过内存地址生成class_ro_t格式的数据呢?

    由于macho文件是在编译期生成的,所以生成class_ro_t格式数据的时期也在编译期通过LLVM生成的

    相关文章

      网友评论

          本文标题:iOS 通过Category的加载来看类的加载

          本文链接:https://www.haomeiwen.com/subject/ohorfltx.html