美文网首页iOS点点滴滴iOS开发进阶
iOS开发进阶:应用启动流程及类的加载流程

iOS开发进阶:应用启动流程及类的加载流程

作者: __Null | 来源:发表于2021-10-23 11:11 被阅读0次

    问自己两个问题:
    1.应用启动在main函数之前到底做了什么事情?
    2.类、分类中load方法的加载顺序怎样的?分类中出现的与主类同名的方法,会调用哪一个呢?

    这些问题,不跟踪一次底层的源码?怎么会领悟得透彻呢?
    我们实现一个类,然后在load方法中打个断点看看:

    load断点调用栈

    这个调用栈的信息就非常丰富,从dyld-_dyld_start开始,经历了一系列步骤,最终进入了load_images,在load_images方法中调用了load方法。

    我们到Apple Open Source网站下载一份最新的dyld源码、system 源码、 dispatch源码、objc源码。(目前最新版本分别是dyld-852.2,Libsystem-1292.120.1,libdispatch-1271.120.2,objc4-824),然后就可以根据load断点打印的调用栈来一点点跟。

    一、dyld内部的主要流程

    __dyld_start开始于dyldStartup.s汇编代码中:

    __dyld_start调用dyldbootstrap::start
    __dyld_start调用main

    这里标注了__dyld_start调用了dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)开始初始化,最后调用了main函数进入主程序。
    所以重点就在dyldbootstrap::start流程中了,由于步骤较多,我们就通过流程图来表示,具体如下:

    dyld内部主要流程

    简单来说:main之前通过,dyld对主程序运行环境进行初始化,生成ImageLoader把动态库生成的image加载到内存中,然后进行链接绑定,接着初始化所有动态库,再接着初始化libSystemlibdispatchlibobjc。其中libobjc的初始化函数_objc_init,初始化运行环境,并且通过_dyld_objc_notify_register(&map_images, load_images, unmap_image)注册读取images,加载images的load函数。

    在进入后续部分之前,我们需要先注意一下_objc_init函数:

    • environ_init():读取影响运行的环境变量
    • tls_init():关于线程key的绑定
    • static_init():运行c++静态构造函数。在dyld调用我们的静态构造函数之前,libdispatch会调用_objc_init()
    • runtime_init():运行时环境初始化,主要是unattachedCategories、allocatedClasses
    • exception_init():objc的异常处理系统
    • cache_t::init():缓存条件初始化
    • _imp_implementationWithBlock_init():启动回调机制
    • _dyld_objc_notify_register(&map_images, load_images, unmap_image):管理文件中和动态库中的符号,然后调用load方法。

    二、map_images流程

    该阶段主要是管理文件中和动态库中的所有符号,包括classprotocolselectorcategory

    2.1.map_images主要用以处理dyld给出的image

    该函数直接进入了map_images_nolock函数。接下来我们先看看这个函数主要作恶什么事情:

    void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) {
    
       if (firstTime) { 
            /*首次执行作一些必要的初始化*/
            preopt_init();
       }
    
       /*通过image的地址,创建header_info信息,并且添加到hList中*/
       /*mach_header对应每个动态库的macho*/
       auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
       hList[hCount++] = hi;
    
       if (firstTime) {
            /*namedSelectors初始化*/
            sel_init(selrefCount);
            
            /*AutoreleasePoolPage::init();//自动释放池初始化
              SideTablesMap.init();//散列表初始化
              _objc_associations_init();//关联表初始化
           */
            arr_init();
        }
         
        if (hCount > 0) {
              /*准备工作完成后就会进入_read_images*/
             _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
    }
    

    2.2.接下来在看_read_images的流程

    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){
          /*先看一下这个宏定义,他是for循环的条件语句,每次取出hi = hList[hIndex],并且hIndex++。所以在下面会看到EACH_HEADER的写法*/
        #define EACH_HEADER \
        hIndex = 0;         \
        hIndex < hCount && (hi = hList[hIndex]); \
        hIndex++
      
      /*1.初始化TaggedPointer, 创建gdb_objc_realized_classes*/
      if (!doneOnce) {
         doneOnce =  true;
    
        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }     
        initializeTaggedPointerObfuscator();
    
        int namedClassesSize = (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
      }
    
    
      /*2.Fix up @selector references*/
      static size_t UnfixedSelectors;
      {
         for (EACH_HEADER) {
             SEL *sels = _getObjc2SelectorRefs(hi, &count);
             for (i = 0; i < count; i++) {
                const char *name = sel_cname(sels[i]);
                /*namedSelectors.get().insert(name)*/
                SEL sel = sel_registerNameNoLock(name, isBundle);
                if (sels[i] != sel) {
                     sels[i] = sel;
                }
            }
        }
     }
       
      /*3.Discover classes. Fix up unresolved future classes. Mark bundle classes*/
      for(EACH_HEADER){
          classref_t const *classlist = _getObjc2ClassList(hi, &count);
          for (i = 0; i < count; i++) {
             Class cls = (Class)classlist[I];
             
              /*readClass:旧类改动后会生成新的类,并重映射到新的类上*/
              /*mangledName !=nil: NXMapInsert(nonMetaClasses(), cls->ISA(), cls) || NXMapInsert(gdb_objc_realized_classes, name, cls)*/
              /*mangledName ==ni:类自身和元类allocatedClasses.insert(cls)*/
             Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
             if (newCls != cls  &&  newCls) {
                  resolvedFutureClasses = (Class *)realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class));
                  resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
             }
           }
        }
    
        /*4.Fix up remapped classes*/
        if (!noClassesRemapped()) {
            for (EACH_HEADER) {
                Class *classrefs = _getObjc2ClassRefs(hi, &count);
                for (i = 0; i < count; i++) {
                    remapClassRef(&classrefs[I]);
                }
                // fixme why doesn't test future1 catch the absence of this?
                classrefs = _getObjc2SuperRefs(hi, &count);
                for (i = 0; i < count; i++) {
                    remapClassRef(&classrefs[I]);
                }
            }
        }
    
        /*5.Fix up old objc_msgSend_fixup call sites*/
        for (EACH_HEADER) {
            message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
            for (i = 0; i < count; i++) {
                fixupMessageRef(refs+i);
            }
        }
    
      /*6.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();
            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);
            }
        }
        //Fix up @protocol references
        for (EACH_HEADER) {
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[I]);
            }
        }
    
        /* 7.Discover categories. Only do this after the initial category attachment has been done*/
        if (didInitialAttachCategories) {
            for (EACH_HEADER) {
                /*cls->isStubClass():objc::unattachedCategories.addForClass(lc, cls);*/
                /*First, register the category with its target class. Then, rebuild the class's method lists (etc) if the class is realized*/
                load_categories_nolock(hi);
            }
        }
    
         /*8.Realize non-lazy classes (for +load methods and static instances)*/
        for (EACH_HEADER) {
            classref_t const *classlist = hi->nlclslist(&count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                addClassTableEntry(cls);
                realizeClassWithoutSwift(cls, nil);
            }
        }
    
        /*9.Realize newly-resolved future classes, in case CF manipulates them*/
        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);
        }
    }
    
    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};
                if (!cls) {
                    // Category's target class is missing (probably weak-linked). Ignore the category.
                    continue;
                }
    
                // Process this category.
                if (cls->isStubClass()) {
                    /* Stub classes are never realized. Stub classes don't know their metaclass until they're  initialized, so we have to add categories with  class methods or properties to the stub itself. methodizeClass() will find them and add them to  the metaclass as appropriate.*/
                    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) {
                     /*实例方法/实例属性:class本身已经实现了,会将相关方法属性添加到class,否则添加到unattachedCategories*/
                        if (cls->isRealized()) {
                            attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                        }else {
                            objc::unattachedCategories.addForClass(lc, cls);
                        }
                    }
    
                    if (cat->classMethods  ||  cat->protocols  ||  (hasClassProperties && cat->_classProperties)){
                         /*类方法/类属性:class-isa本身已经实现了,会将相关方法属性添加到metadata-class,否则添加到unattachedCategories*/
                        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));
    }
    

    三、load_images流程

    该阶段主要是准备load方法,包括主类和分类中的,然后调用load方法。

    入口函数,准备load方法,调用load方法
    void load_images(const char *path __unused, const struct mach_header *mh){
        prepare_load_methods((const headerType *)mh);// Discover +load methods
        call_load_methods(); // Call +load methods (without classLock - re-entrant)
    }
    

    3.1.准备所有的load方法

    包含load方法的class,category添加到对应的表中:

    void prepare_load_methods(const headerType *mhdr){
        size_t count, I;
    
        runtimeLock.assertLocked();
    
        /*superclsss->class:添加到loadable_classes表中,实体类型loadable_class*/
        classref_t const *classlist =  _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            schedule_class_load(remapClass(classlist[i]));
        }
    
        /*category:添加到loadable_categories表中,实体类型loadable_category*/
        category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
        for (i = 0; i < count; i++) {
            add_category_to_loadable_list(categorylist[I]);
        }
    }
    

    class添加到loadable_classes表中,加入的是loadable_class类型(包含cls和load-method, getLoadMethod就是查找load方法);分类添加到loadable_categories表中,加入的是loadable_category类型(包含category和load-method)

    static void schedule_class_load(Class cls){
       ...
        /*先插入superclass, 再插入class!!load方法的调用顺序*/
        schedule_class_load(cls->getSuperclass());
        add_class_to_loadable_list(cls);
       ...
    }
    
    /*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_classes_used作为计数器*/
    void add_class_to_loadable_list(Class cls){
        IMP method = cls->getLoadMethod();
        if (!method) return;  // Don't bother if cls has no +load method
        if (loadable_classes_used == loadable_classes_allocated) {
            loadable_classes_allocated = loadable_classes_allocated*2 + 16;
            loadable_classes = (struct loadable_class *) realloc(loadable_classes,  loadable_classes_allocated *
                                  sizeof(struct loadable_class));
        }
        loadable_classes[loadable_classes_used].cls = cls;
        loadable_classes[loadable_classes_used].method = method;
        loadable_classes_used++;
    }
    
    /*没有load方法的类不会加进来,会在已经申请的内存用完的情况继续扩容,loadable_categories_used作为计数器*/
    void add_category_to_loadable_list(Category cat){
        IMP method = _category_getLoadMethod(cat);
        if (!method) return;  // Don't bother if cat has no +load method
        if (loadable_categories_used == loadable_categories_allocated) {
            loadable_categories_allocated = loadable_categories_allocated*2 + 16;
            loadable_categories = (struct loadable_category *) realloc(loadable_categories, loadable_categories_allocated *
                                  sizeof(struct loadable_category));
        }
    
        loadable_categories[loadable_categories_used].cat = cat;
        loadable_categories[loadable_categories_used].method = method;
        loadable_categories_used++;
    }
    
    /* loadable_class结构体*/
    struct loadable_class {
        Class cls;  // may be nil
        IMP method;
    };
    
    /* loadable_category结构体*/
    struct loadable_category {
        Category cat;  // may be nil
        IMP method;
    };
    
    

    3.2.调用load方法

    void call_load_methods(void){
        ...
        do {
           
            while (loadable_classes_used > 0) { call_class_loads();  }
    
            // 2. 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);
        ...
    }
    
    //调用class-load方法,loadable_classes_used=0
    static void call_class_loads(void){
       ...
        for (i = 0; i < used; i++) {
            Class cls = classes[i].cls;
            load_method_t load_method = (load_method_t)classes[i].method;
            if (!cls) continue; 
            (*load_method)(cls, @selector(load));
        }
        ...
    }
    
    //调用category-load方法,loadable_categories_used=0
    static bool call_category_loads(void){
        ...
        for (i = 0; i < used; i++) {
            Category cat = cats[i].cat;
            load_method_t load_method = (load_method_t)cats[i].method;
            Class cls;
            if (!cat) continue;
          
            cls = _category_getClass(cat);
            if (cls  &&  cls->isLoadable()) {
                (*load_method)(cls, @selector(load));
                cats[i].cat = nil;
            }
        }
        ...
    }
    

    非懒加载的类,也就是有load方法的类,回在main之前完成类的初始化,具体涉及到函数包括:

    • readClass
    • _getObjcNolazyClassList
    • realizeClassWithoutSwift
    • methodizeClass
      懒加载的类,会在第一次消息的时候完成类的初始化:
    • lookUpImpOrForward
    • realizeClassMaybeSwiftMaybeRelock
    • realizeClassWithoutSwift
    • methodizeClass
      关于map_images和load_images中较为详细的流程可以看下图:
      map & load_images.png

    四、load、initialize方法加载流程实践

    通过上面的分析我们知道了App的启动流程中dyld做了哪些事情,那么:

    +load+initialize分别是什么时候调用的呢?以及他们在父类,子类,分类中调用的顺序是什么样的呢?
    我们先准备如下几个ClassCategory
    Class-NXPerson代码:

    @interface NXPerson : NSObject
    @property (nonatomic, copy) NSString *name;
    - (void)run;
    @end
    
    @implementation NXPerson
    + (void)load {NSLog(@"%s",__func__);}
    + (void)initialize {NSLog(@"%s",__func__);}
    - (void)run{NSLog(@"%s",__func__);}
    @end
    

    Class-NXTeacher代码:

    @interface NXTeacher : NXPerson
    - (void)teach;
    @end
    
    @implementation NXTeacher
    + (void)load {NSLog(@"%s",__func__);}
    + (void)initialize {NSLog(@"%s",__func__);}
    - (void)teach{NSLog(@"%s",__func__);}
    @end
    

    Class-NXStudent代码:

    @interface NXStudent : NXPerson
    - (void)study;
    @end
    
    @implementation NXStudent
    + (void)load{NSLog(@"%s",__func__);}
    + (void)initialize {NSLog(@"%s",__func__);}
    - (void)study{NSLog(@"%s",__func__);}
    @end
    

    Category-NXPerson(Category)代码:

    @interface NXPerson(Category)
    @end
    
    @implementation NXPerson(Category)
    + (void)load{NSLog(@"%s",__func__);}
    + (void)initialize{NSLog(@"%s",__func__);}
    - (void)run {NSLog(@"%s",__func__);}
    @end
    

    Category-NXTeacher(Category)代码:

    @interface NXTeacher(Category)
    @end
    @implementation NXTeacher(Category)
    + (void)load {NSLog(@"%s",__func__);}
    + (void)initialize{NSLog(@"%s",__func__);}
    - (void)teach {NSLog(@"%s",__func__);}
    @end
    

    Category-NXStudent(Category)代码:

    @interface NXStudent(Category)
    @end
    @implementation NXStudent(Category)
    + (void)load {NSLog(@"%s",__func__);}
    + (void)initialize {NSLog(@"%s",__func__);}
    - (void)study {NSLog(@"%s",__func__);}
    @end
    

    代码如上,一共三个类NXPersonNXTeacherNXStudent,后2者继承于前者;三个分类NXPerson(Category)NXTeacher(Category)NXStudent(Category)。每个主类分别实现了-run-teach-study+load+initialize方法。每个分类中也实现了主类的方法和+load+initialize方法。

    4.1.+load方法

    首先我们在没有任何调用的情况下的运行代码:


    +load方法调用

    探索:
    1.ClassCategory+load方法都被调用了,+initialize方法没有被调用。
    2.Class是不是一定在Category前面打印呢?我们将NXPerson(Category)放在NXStudentNXTeacher之前,继续运行代码发现Class+load依旧比Category+load更先调用。
    3.我们更换几个Class的顺序,发现NXPerson+load永远在最前,同级子类加载的顺序跟代码的顺序有关。至此我们可以推断父类的+load先于子类调用。
    4.我们更换几个Category的顺序,发现谁的代码在前谁先打印,即分类的+load方法加载的顺序是根据代码读取的顺序来决定的。

    以上结论其实是可以在objc源码中找到答案的:在call_load_methods中我们也可以看到call_class_loads先于call_category_loads调用(源码如下),这个决定了所有的父类的+load更先读取。

    void call_load_methods(void){
        ...
       //调用loadable_classes中`loadable_class`绑定的`load`方法
       call_class_loads();
       //调用loadable_categories中`loadable_category`绑定的`load`方法
       call_category_loads();
        ....
    }
    

    4.2.initialize方法

    我们添加如下代码到main中:

    NXTeacher *p = [[NXTeacher alloc] init];
    

    打印结果如下:

     [NXPerson(Category) initialize]
     [NXTeacher(Category) initialize]
    

    反复运行打印的只有分类的+initialize,以及分类对应类的父类的分类的+initialize,并且父类先于之类调用.

    探索:
    1.+initialize方法默认会在main之后,第一次访问该类的时候调用。通常首次访问的方法+alloc+self+class+new等等类方法。
    2.initialize会先调用父类,再调用子类。如果同级有分类,则分类的方法会覆盖掉主类的方法,多个分类的会调用最后一个加载进内存的分类的方法。

    上面我们讲到最先访问到一个类的方式通常是+alloc+self+class+new方法,接下来我们就初始化一个[NXTeacher alloc],而Objective-C的方法调用,底层都是通过消息转发objc_msgSend实现的,那么[NXTeacher alloc]会被转化为objc_msgSend(NXTeacher, @selector(alloc))来进入汇编代码实现的快速查找流程从缓存中查找,那么第一次调用缓存中肯定没有alloc方法的;缓存中没有找到,则会进入慢速查找流程lookUpImpOrForward

    NEVER_INLINE IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){
        ...
        cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
        ...
    }
    
    static Class realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize){
        if (slowpath(!cls->isRealized())) {
            /*实现该类*/
            cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        }
    
        if (slowpath(initialize && !cls->isInitialized())) {
           /*初始化该类*/
            cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        }
        return cls;
    }
    

    4.2.1.分类方法是怎么添加的?

    static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) {
        return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
    }
    
    static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked){
        ...
        if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
            realizeClassWithoutSwift(cls, nil);
        } 
        else {
            cls = realizeSwiftClass(cls);
        }
        return cls;
    }
    
    static Class realizeClassWithoutSwift(Class cls, Class previously){
        ...
        supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
        metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
        ...
        // Update superclass and metaclass in case of remapping
        cls->setSuperclass(supercls);
        cls->initClassIsa(metacls);
        ...
        // Attach categories
        methodizeClass(cls, previously);
        return cls;
    }
    
    static void methodizeClass(Class cls, Class previously){
        ...
        // Attach categories
        if (previously) {
            if (isMeta) {
                //类方法添加到元类上
                objc::unattachedCategories.attachToClass(cls, previously,
                                                         ATTACH_METACLASS);
            } else {
                //实例方法添加到类上
                objc::unattachedCategories.attachToClass(cls, previously,
                                                         ATTACH_CLASS_AND_METACLASS);
            }
        }
        objc::unattachedCategories.attachToClass(cls, cls,
                                                 isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
    }
    
    void attachToClass(Class cls, Class previously, int flags){
         ...
         auto &map = get();
         auto it = map.find(previously);
    
         if (it != map.end()) {
             category_list &list = it->second;
             if (flags & ATTACH_CLASS_AND_METACLASS) {
                 int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
                 attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
                 attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
             } 
             else {
                 attachCategories(cls, list.array(), list.count(), flags);
             }
             map.erase(it);
        }
    }
    
    static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                     int flags){
        ...
        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, protocol_list_t
        }
    
        if (mcount > 0) {
            prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                               NO, fromBundle, __func__);
            rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
            ...
        }
        ...
    }
    
    //这个方法里边在插入的时候
    void attachLists(List* const * addedLists, uint32_t addedCount) {
       ...
       // many lists -> many lists
       uint32_t oldCount = array()->count;
       uint32_t newCount = oldCount + addedCount;
       array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
       newArray->count = newCount;
       array()->count = newCount;
    
       /*第1个for循环相当于将老数据往后平移了addedCount个位置*/
       for (int i = oldCount - 1; i >= 0; I--)
           newArray->lists[i + addedCount] = array()->lists[I];
    
       /*第2个for循环相当于将新数据按顺序放在前面*/
       for (unsigned i = 0; i < addedCount; I++)
           newArray->lists[i] = addedLists[I];
    
       free(array());
       setArray(newArray);
       validate();
       ...
    }
    

    如此以来按顺序查找的时候,那么排在前面的category方法会被优先调用,categoey-initialze会被调用,而主类没有被调用的真正原因。

    4.2.2.initialize调用顺序

    static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock{
        return initializeAndMaybeRelock(cls, obj, lock, true);
    }
    
    static Class initializeAndMaybeRelock(Class cls, id inst, mutex_t& lock, bool leaveLocked){
        ...
        initializeNonMetaClass(nonmeta);
        ...
    }
    
    void initializeNonMetaClass(Class cls){
        ...
         /*重点!!!,这里会先调用父类的initialize方法*/
        supercls = cls->getSuperclass();
        if (supercls  &&  !supercls->isInitialized()) {
            initializeNonMetaClass(supercls);
        }
        allInitialize(cls);
    }
    
    void callInitialize(Class cls){
        ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    }
    

    通过以上的流程,我们可以看出initialize方法的调用时先调用父类,再调用当前类的。

    总结:
    关于load方法:
    1.classcategory+load方法都会被调用,是在main之前被调用。
    2.调用顺序先主类的+load,再调用分类的+load。主类的调用顺序为先superclass,再subclass。分类的+load是根据代码读取的顺序。子类的+load可能会在前面。
    关于initialize方法:
    1.如果有分类,那么分类的方法列表会加载进主类的方法列表中,并且放在方法列表的头部,按照代码的读取顺序,最后读取到分类方法会放在最前面。按照顺序查找则最后加载进去的方法会被优先查找到(这也就是主类的同名方法会被分类【覆盖掉】的原因)。分类initialize会被调用,而主类的initialize不会被调用。
    2.调用顺序依然是先superclass,再subclass

    相关文章

      网友评论

        本文标题:iOS开发进阶:应用启动流程及类的加载流程

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