美文网首页iOS底层收集
iOS进阶-08应用程序加载-map_Images&Load_I

iOS进阶-08应用程序加载-map_Images&Load_I

作者: ricefun | 来源:发表于2020-02-18 14:29 被阅读0次

    上一章介绍了应用加载的基本流程,即函数void _objc_init(void)内部的实现,其内部最主要的函数_dyld_objc_notify_register(&map_images, load_images, unmap_image);只是说了注册方法指针回调,用于程序调用,那这个方法内部的map_images, load_images,unmap_image又是做了一些什么呢?

    map_images

    我将选取部分源码中的函数进行说明,在函数中重要的位置会有相应的注释,后面不再一一解释

    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);
    }
    
    map_images_nolock()
    void 
    map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    {
        static bool firstTime = YES;
        header_info *hList[mhCount];
        uint32_t hCount;
        size_t selrefCount = 0;
    ...
    ...
        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
        firstTime = NO;
    }
    
    _read_images源码简化后的代码
    void _read_images {
        
        // 1:第一次进来 - 开始创建表
        // gdb_objc_realized_classes : 所有类的表 - 包括实现的和没有实现的
        // allocatedClasses: 包含用objc_allocateClassPair分配的所有类(和元类)的表。(已分配)
        if (!doneOnce) {
               doneOnce = YES;
            // namedClasses
            // Preoptimized classes don't go in this table.
            // 4/3 is NXMapTable's load factor
            int namedClassesSize =
                (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
            gdb_objc_realized_classes =
                NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
            
            allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
        }
        
        // 2:类处理
        for (i = 0; i < count; i++) {
          Class cls = (Class)classlist[i];
          Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
        }
        
        // 3: 方法编号处理
        for (EACH_HEADER) {
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
              const char *name = sel_cname(sels[i]);
              sels[i] = sel_registerNameNoLock(name, isBundle);
            }
        }
    
        // 4: 协议处理
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            NXMapTable *protocol_map = protocols();
            protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                readProtocol(protolist[i], cls, protocol_map,
                             isPreoptimized, isBundle);
            }
        }
        
        // 5: 非懒加载类处理
        for (EACH_HEADER) {
          classref_t *classlist =
              _getObjc2NonlazyClassList(hi, &count);
          addClassTableEntry(cls);
          realizeClassWithoutSwift(cls);
        }
        
        // 6: 待处理的类
        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);
                cls->setInstancesRequireRawIsa(false/*inherited*/);
            }
            free(resolvedFutureClasses);
        }
        
        // 7:分类处理
       for (EACH_HEADER) {
           category_t **catlist =
               _getObjc2CategoryList(hi, &count);
           bool hasClassProperties = hi->info()->hasCategoryClassProperties();
           for (i = 0; i < count; i++) {
               category_t *cat = catlist[i];
               Class cls = remapClass(cat->cls);
           }
       }
    
    }
    
    _read_images 之 readClass()
    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName = cls->mangledName();
        if (missingWeakSuperclass(cls)) {
            if (PrintConnecting) {
                _objc_inform("CLASS: IGNORING class '%s' with "
                             "missing weak-linked superclass", 
                             cls->nameForLogging());
            }
            addRemappedClass(cls, nil);
            cls->superclass = nil;
            return nil;
        }
    
        cls->fixupBackwardDeployingStableSwift();
        Class replacing = nil;
        if (Class newCls = popFutureNamedClass(mangledName)) {
            if (newCls->isAnySwift()) {
                _objc_fatal("Can't complete future class request for '%s' "
                            "because the real class is too big.", 
                            cls->nameForLogging());
            }
            
            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro;
            memcpy(newCls, cls, sizeof(objc_class));
            rw->ro = (class_ro_t *)newCls->data();
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->name);
            free((void *)old_ro);
            
            addRemappedClass(cls, newCls);
            
            replacing = cls;
            cls = newCls;
        }
        
        if (headerIsPreoptimized  &&  !replacing) {
    assert(getClassExceptSomeSwift(mangledName));
        } else {
            addNamedClass(cls, mangledName, replacing);
            addClassTableEntry(cls);
        }
    
        // for future reference: shared cache never contains MH_BUNDLEs
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        
        return cls;
    }
    
    

    readClass()方法中主要做了这几件事:
    1.判断下是不是后期要处理的类,读取Class的data() - 设置ro/rw

    1. addNamedClass(cls, mangledName, replacing);
    2. addClassTableEntry(cls);
      4.插入到表中
    _read_images 之 static Class realizeClassWithoutSwift(Class cls)
    static Class realizeClassWithoutSwift(Class cls)
    {
        runtimeLock.assertLocked();
    
        const class_ro_t *ro;
        class_rw_t *rw;
        Class supercls;
        Class metacls;
        bool isMeta;
    
        if (!cls) return nil;
        if (cls->isRealized()) return cls;
        assert(cls == remapClass(cls));
    
        // fixme verify class is not in an un-dlopened part of the shared cache?
    
        ro = (const class_ro_t *)cls->data();
        if (ro->flags & RO_FUTURE) {
            // This was a future class. rw data is already allocated.
            rw = cls->data();
            ro = cls->data()->ro;
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
    
        isMeta = ro->flags & RO_META;
    
        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6
    
    
        // 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.
        supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
    
    #if SUPPORT_NONPOINTER_ISA
        // 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  &&  !(ro->flags & RO_META)  &&  
                 0 == strcmp(ro->name, "OS_object")) 
        {
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&  
                 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->setInstancesRequireRawIsa(rawIsaIsInherited);
        }
    // SUPPORT_NONPOINTER_ISA
    #endif
    
        // Update superclass and metaclass in case of remapping
        cls->superclass = 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);
        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;
        }
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
        methodizeClass(cls);
        return cls;
    }
    
    1. 读取class的data()
    2. ro/rw的创建
      3.父类和元类的实现内部(递归)分别是:supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
      metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
      4.配置父类和元类的归属关系:;cls->superclass = supercls;;
      ;cls->initClassIsa(metacls);;
      5.将此类连接到其超类的子类列表
    if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
    realizeClassWithoutSwift

    之methodizeClass(cls);

    static void methodizeClass(Class cls)
    {
        runtimeLock.assertLocked();
    
        bool isMeta = cls->isMetaClass();
        auto rw = cls->data();
        auto ro = rw->ro;
    
        // 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));
            rw->methods.attachLists(&list, 1);
        }
    
        property_list_t *proplist = ro->baseProperties;
        if (proplist) {
            rw->properties.attachLists(&proplist, 1);
        }
    
        protocol_list_t *protolist = ro->baseProtocols;
        if (protolist) {
            rw->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, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
        }
    
        // Attach categories.
        category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
        attachCategories(cls, cats, false /*don't flush caches*/);
    
        if (PrintConnecting) {
            if (cats) {
                for (uint32_t i = 0; i < cats->count; i++) {
                    _objc_inform("CLASS: attached category %c%s(%s)", 
                                 isMeta ? '+' : '-', 
                                 cls->nameForLogging(), cats->list[i].cat->name);
                }
            }
        }
        
        if (cats) free(cats);
    
    #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
    }
    

    把ro数据写到rw中:method_list_t、property_list_t、protocol_list_t

    methodizeClass 之 attachLists()
    void attachLists(List* const * addedLists, uint32_t addedCount) {
            if (addedCount == 0) return;
    
            if (hasArray()) {
                // 多对多
                uint32_t oldCount = array()->count;
                uint32_t newCount = oldCount + addedCount;
                setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
                array()->count = newCount;
                memmove(array()->lists + addedCount, array()->lists, 
                        oldCount * sizeof(array()->lists[0]));
                memcpy(array()->lists, addedLists, 
                       addedCount * sizeof(array()->lists[0]));
            }
            else if (!list  &&  addedCount == 1) {
                // 0对多
                list = addedLists[0];
            } 
            else {
                // 1对多
                List* oldList = list;
                uint32_t oldCount = oldList ? 1 : 0;
                uint32_t newCount = oldCount + addedCount;
                setArray((array_t *)malloc(array_t::byteSize(newCount)));
                array()->count = newCount;
                if (oldList) array()->lists[addedCount] = oldList;
                memcpy(array()->lists, addedLists, 
                       addedCount * sizeof(array()->lists[0]));
            }
        }
    
    1.贴的时候是从源内存地址的起始位置开始复制的,并且是二维数组的形式; array_t原理图

    2.是一个个先move然后copy

    memmove(array()->lists + addedCount, array()->lists, 
                        oldCount * sizeof(array()->lists[0]));
    memcpy(array()->lists, addedLists, 
                       addedCount * sizeof(array()->lists[0]));
    

    3.其他地方也会用到attachLists()eg:

    • 类的加载,处理方法-属性-协议-methodizeClass
    • 方法的添加 addMethod
    • 添加属性_class_addProperty
    • 添加协议class_addProtocol
    • 分类的加载attachCategories
      为什么?因为都有相同二位数组结构:entsize_list_tt<method_t, method_list_t, 0x3>
    map_images小节

    map_images主要做了

    • 初始化类:realiseClass 设置rw、ro
    • 处理分类:把分类中的方法协议、属性添加到类中
    • 加载到相应的哈希表中

    懒加载类和非懒加载类

    懒加载类和非懒加载类的表面区别在于是否实现了load方法,如果

    • 实现了load方法就是非懒加载类
    • 未实现了load方法就是懒加载类
      但内层区在于属非懒加载类直接走上节课的realizeClassWithoutSwift 去加载数据,懒加载类 在lookupimpOrforward中 有
      if (!cls->isRealized())判断条件,如果用到时候没有实现过则进入实现流程,如果实现过则跳过判断,进行下面的流程。
    if (!cls->isRealized()) {
    cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
    // runtimeLock may have been dropped but is now locked again
    }
    
    主类和分类懒加载分析

    上面只提到load方法造成了懒加载和非懒加载的区别,其实实际当中会有4种情况

      1. 懒加载类 + 非懒加载分类
        主类在发送消息的时候才有 — 但是分类提前了 — 需要加载 —-read_image —addUnattacheCategoryForClass —但是没有实现类 ——prepare_load_methods —提前实现了主类realizeClassWithoutSwift
    • 2.非懒加载类 + 非懒加载分类
      readImages — realizeClassWithoutSwift —— methodLizeClass — addUnattacheCategoryForClass —判断主类是否实现了,但是这时候主类已经实现了,if(cls —> isRealize){ remethodizeClass
      }
      加载分类进来
      cls —-> demangleName () 用这个,不能用Name
      addUnattachedCategoryForClass(cat, cls, hi);
      const char *cname = ro->name;
      const char *oname = "LGTeacher";//LGTeacher
      if (cname && (strcmp(cname, oname) == 0)) {
      // printf("_read_images - _getObjc2ClassList 类名 :%s - %p\n",cname,cls);
      }
      if(cls —> isRealize)
      {
      remethodizeClass(cls);
      classExists == YES;
      }
      分类没有实现load —— 编译处理 — data() —> ro

    • 3.非懒加载类 + 懒加载分类
      readImages — realizeClassWithoutSwift —— methodLizeClass — 不需要添加表 ——直接 data() ——> ro

    • 4.懒加载类 + 懒加载分类
      消息发送的时候 ——lookupImpOrForward —— realizeClassWithoutSwift —— methodLizeClass — addUnattacheCategoryForClass 不进来 ——直接走data()

    该部分参考链接 https://juejin.im/post/5e1c36fcf265da3df47adc1f

    面试题

    1.runtime底层api使用?
    #import <objc/runtime.h>
    #import "LGTeacher.h"
    
    void lgSetter(NSString *value){
        printf("%s/n",__func__);
    }
    
    NSString *lgName(){
        printf("%s/n",__func__);
        return @"master NB";
    }
    
    void lg_class_addProperty(Class targetClass , const char *propertyName){
        
        objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
        objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
        objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
        objc_property_attribute_t backingivar  = { "V", [NSString stringWithFormat:@"_%@",[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]].UTF8String };  //variable name
        objc_property_attribute_t attrs[] = {type, ownership0, ownership,backingivar};
    
        class_addProperty(targetClass, propertyName, attrs, 4);
    
    }
    
    void lg_printerProperty(Class targetClass){
        unsigned int outCount, i;
        objc_property_t *properties = class_copyPropertyList(targetClass, &outCount);
        for (i = 0; i < outCount; i++) {
            objc_property_t property = properties[i];
            fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
        }
    }
    
    
    // class_data_bits_t
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
    
        // ro - rw  - 有没有面试题
            
            // 1: 动态创建类
            Class LGPerson = objc_allocateClassPair([NSObject class], "LGPerson", 0);
            // 2: 添加成员变量 1<<aligment
            // ivar - ro - ivarlist
            class_addIvar(LGPerson, "lgName", sizeof(NSString *), log2(sizeof(NSString *)), "@");
            // 3: 注册到内存
            objc_registerClassPair(LGPerson);
            
            // cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
            // cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
           
            // 3.1 添加property - rw
            lg_class_addProperty(LGPerson, "subject");
            lg_printerProperty([LGTeacher class]);
            lg_printerProperty(LGPerson);
    
            // 3.2 添加setter  +  getter 方法
            class_addMethod(LGPerson, @selector(setSubject:), (IMP)lgSetter, "v@:@");
            class_addMethod(LGPerson, @selector(subject), (IMP)lgName, "@@:");
    
            // 开始使用
            id person = [LGPerson alloc];
            [person setValue:@"KC" forKey:@"lgName"];
            NSLog(@"%@",[person valueForKey:@"lgName"]);
            
            id teacher = [LGTeacher alloc];
            [teacher setValue:@"iOS" forKey:@"subject"];
            NSLog(@"%@",[teacher valueForKey:@"subject"]);
            
            [person setValue:@"master" forKey:@"subject"];
            NSLog(@"%@",[person valueForKey:@"subject"]);
        }
        return 0;
    }
    
    
    // 相关api 的注释
    
    /**
     * 创建类对
     *superClass: 父类,传Nil会创建一个新的根类
     *name: 类名
     *extraBytes: 0
     *return:返回新类,创建失败返回Nil,如果类名已经存在,则创建失败
      objc_allocateClassPair(<#Class  _Nullable __unsafe_unretained superclass#>, <#const char * _Nonnull name#>, <#size_t extraBytes#>)
     */
    
    
    /**
     *添加成员变量
     *
     *cls 往哪个类添加
     *name 添加的名字
     *size 大小
     *alignment 对齐处理方式
     *types 签名
     *
     *这个函数只能在objc_allocateClassPair和objc_registerClassPair之前调用。不支持向现有类添加一个实例变量。
     *这个类不能是元类。不支持在元类中添加一个实例变量。
     *实例变量的最小对齐为1 << align。实例变量的最小对齐依赖于ivar的类型和机器架构。对于任何指针类型的变量,请通过log2(sizeof(pointer_type))。
      class_addIvar(<#Class  _Nullable __unsafe_unretained cls#>, <#const char * _Nonnull name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char * _Nullable types#>)
     */
    
    /**
     *往内存注册类
     *
     * cls 要注册的类
     *
     * objc_registerClassPair(<#Class  _Nonnull __unsafe_unretained cls#>)
     */
    
    
    /**
     *往类里面添加方法
     *
     *cls 要添加方法的类
     *sel 方法编号
     *imp 函数实现指针
     *types 签名
     *
     *class_addMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)
     */
    
    
    
    /**
     *往类里面添加属性
     *
     *cls 要添加属性的类
     *name 属性名字
     *attributes 属性的属性数组。
     *attriCount 属性中属性的数量。
     *
     *class_addProperty(<#Class  _Nullable __unsafe_unretained cls#>, <#const char * _Nonnull name#>, <#const objc_property_attribute_t * _Nullable attributes#>, <#unsigned int attributeCount#>)
     */
    
    2.类拓展&分类

    类拓展:匿名分类,增加属性和方法;
    在上一章我们知道分类是在read_images中加载到内存段的,而类拓展会在编译时作为类的一部分编译到内存段中,所以可以直接读取ro

    !!! 如果类拓展没有被import到.m文件中时,是不会被编译到当前类的内存段的

    分类如何增加属性?
    1.通过关联对象

    2.系统会管理一张关联对象的大的HashMap,然后每个类(拥有关联对象的类)又会有一张小的HashMap管理关联属性;以对象的指针为key找到小表然后将value、“name”、关联策略存起来,取值过程刚好相反 截屏2020-02-1912.16.33.png
    3.当对象delloc的时候,又会去系统中查找当前对象有没有关联对象,有的话就先移除,之后再delloc
    3.load方法加载顺序

    先看源码:
    load_images()

    void
    load_images(const char *path __unused, const struct mach_header *mh)
    {
        // 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);
            //准备非懒加载类load方法,将其放在一个数组中
            prepare_load_methods((const headerType *)mh);
        }
       //遍历加载load方法
        call_load_methods();
    }
    
    call_load_methods();
    
    void call_load_methods(void)
    {
        static bool loading = NO;
        bool more_categories;
    
        loadMethodLock.assertLocked();
    
        // Re-entrant calls do nothing; the outermost call will finish the job.
        if (loading) return;
        loading = YES;
    
        void *pool = objc_autoreleasePoolPush();
    
        do {
            // 1.先调用主类的load方法
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2.再调用分类的load方法 Call category +loads ONCE
            more_categories = call_category_loads();
    
            // 3. Run more +loads if there are classes OR more untried categories
        } while (loadable_classes_used > 0  ||  more_categories);
    
        objc_autoreleasePoolPop(pool);
    
        loading = NO;
    }
    
    

    从上面的源码可以得到load方法:

    • 主类的load方法会先于分类load方法
    • 主类和分类拥有相同方法时,分类会被调用,因为每个分类的方法会通过attachlist方法以数组的形式插入到二维数组的前面,所以会造成分类覆盖主类的假象
    • 如何修改分类load方法顺序 - target - Bulid Phases - Complies Sources,shuoebi 就是调整编译顺序
    initialize在什么时候调用
    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        IMP imp = nil;
        bool triedResolver = NO;
    
        ...
        ...
    
        if (initialize && !cls->isInitialized()) {
            cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
            // runtimeLock may have been dropped but is now locked again
    
            // If sel == initialize, class_initialize will send +initialize and 
            // then the messenger will send +initialize again after this 
            // procedure finishes. Of course, if this is not being called 
            // from the messenger then it won't happen. 2778172
        }
        ...
        ...
        return imp;
    }
    

    可以看到initialize方法在lookUpImpOrForward在这个方法后调用;所以我们总结下

    • 晚于load方法调用
    • +initialize方法
      通过消息机制调用(objc_msgSend),当子类没有实现+initialize方法时,会调用父类的initialize方法
    • 先调用父类的+initialize方法,后调用子类的+initialize方法
    • 如果一个类有分类,那么会调用最后编译的分类实现的

    相关文章

      网友评论

        本文标题:iOS进阶-08应用程序加载-map_Images&Load_I

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