+load

作者: 石头89 | 来源:发表于2019-07-30 18:09 被阅读0次

    +load

    +load 方法的调用时机

    • +load 方法在 Runtime 加载 Class、Category 时调用。
    • 每个 Class、Category 的 +load 方法,在程序运行过程中只调用一次。

    +load 方法的调用顺序

    • 先调用 Class 的 +load 方法
      • 按照编译的先后顺序调用。
      • 调用子类的 +load 方法之前会先调用父类的 +load 方法。
    • 再调用 Category 的 +load 方法
      • 按照编译的先后顺序调用。

    +load 方法调用的相关源码

    objc4 源码解读顺序(objc-os.mm):

    1. _objc_init
    2. load_images
    3. prepare_load_methods
    • schedule_class_load
    • add_class_to_loadable_list
    • add_category_to_loadable_list
    1. call_load_methods
    • call_class_loads
    • call_category_loads
    • (*load_method)(cls, SEL_load)

    +load 方法是根据方法地址直接调用的,不经过 obj_msgSend 函数调用。

    /***********************************************************************
    * _objc_init
    * Bootstrap initialization. Registers our image notifier with dyld.
    * Called by libSystem BEFORE library initialization time
    **********************************************************************/
    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();
        tls_init();
        static_init();
        lock_init();
        exception_init();
    
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    }
    
    /***********************************************************************
    * load_images
    * Process +load in the given images which are being mapped in by dyld.
    *
    * Locking: write-locks runtimeLock and loadMethodLock
    **********************************************************************/
    extern bool hasLoadMethods(const headerType *mhdr);
    extern void prepare_load_methods(const headerType *mhdr);
    
    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
        {
            rwlock_writer_t lock2(runtimeLock);
            prepare_load_methods((const headerType *)mh);
        }
    
        // Call +load methods (without runtimeLock - re-entrant)
        call_load_methods();
    }
    
    typedef void(*load_method_t)(id, SEL);
    
    /***********************************************************************
    * call_load_methods
    * Call all pending class and category +load methods.
    * Class +load methods are called superclass-first. 
    * Category +load methods are not called until after the parent class's +load.
    * 
    * This method must be RE-ENTRANT, because a +load could trigger 
    * more image mapping. In addition, the superclass-first ordering 
    * must be preserved in the face of re-entrant calls. Therefore, 
    * only the OUTERMOST call of this function will do anything, and 
    * that call will handle all loadable classes, even those generated 
    * while it was running.
    *
    * The sequence below preserves +load ordering in the face of 
    * image loading during a +load, and make sure that no 
    * +load method is forgotten because it was added during 
    * a +load call.
    * Sequence:
    * 1. Repeatedly call class +loads until there aren't any more
    * 2. Call category +loads ONCE.
    * 3. Run more +loads if:
    *    (a) there are more classes to load, OR
    *    (b) there are some potential category +loads that have 
    *        still never been attempted.
    * Category +loads are only run once to ensure "parent class first" 
    * ordering, even if a category +load triggers a new loadable class 
    * and a new loadable category attached to that class. 
    *
    * Locking: loadMethodLock must be held by the caller 
    *   All other locks must not be held.
    **********************************************************************/
    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. Repeatedly call class +loads until there aren't any more
            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);
    
        objc_autoreleasePoolPop(pool);
    
        loading = NO;
    }
    
    /***********************************************************************
    * call_class_loads
    * Call all pending class +load methods.
    * If new classes become loadable, +load is NOT called for them.
    *
    * Called only by call_load_methods().
    **********************************************************************/
    static void call_class_loads(void)
    {
        int i;
        
        // Detach current loadable list.
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // Call all +loads for the detached list.
        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; 
    
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
            }
            (*load_method)(cls, SEL_load);
        }
        
        // Destroy the detached list.
        if (classes) free(classes);
    }
    
    /***********************************************************************
    * call_category_loads
    * Call some pending category +load methods.
    * The parent class of the +load-implementing categories has all of 
    *   its categories attached, in case some are lazily waiting for +initalize.
    * Don't call +load unless the parent class is connected.
    * If new categories become loadable, +load is NOT called, and they 
    *   are added to the end of the loadable list, and we return TRUE.
    * Return FALSE if no new categories became loadable.
    *
    * Called only by call_load_methods().
    **********************************************************************/
    static bool call_category_loads(void)
    {
        int i, shift;
        bool new_categories_added = NO;
        
        // Detach current loadable list.
        struct loadable_category *cats = loadable_categories;
        int used = loadable_categories_used;
        int allocated = loadable_categories_allocated;
        loadable_categories = nil;
        loadable_categories_allocated = 0;
        loadable_categories_used = 0;
    
        // Call all +loads for the detached list.
        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()) {
                if (PrintLoading) {
                    _objc_inform("LOAD: +[%s(%s) load]\n", 
                                 cls->nameForLogging(), 
                                 _category_getName(cat));
                }
                (*load_method)(cls, SEL_load);
                cats[i].cat = nil;
            }
        }
    
        // Compact detached list (order-preserving)
        shift = 0;
        for (i = 0; i < used; i++) {
            if (cats[i].cat) {
                cats[i-shift] = cats[i];
            } else {
                shift++;
            }
        }
        used -= shift;
    
        // Copy any new +load candidates from the new list to the detached list.
        new_categories_added = (loadable_categories_used > 0);
        for (i = 0; i < loadable_categories_used; i++) {
            if (used == allocated) {
                allocated = allocated*2 + 16;
                cats = (struct loadable_category *)
                    realloc(cats, allocated *
                                      sizeof(struct loadable_category));
            }
            cats[used++] = loadable_categories[i];
        }
    
        // Destroy the new list.
        if (loadable_categories) free(loadable_categories);
    
        // Reattach the (now augmented) detached list. 
        // But if there's nothing left to load, destroy the list.
        if (used) {
            loadable_categories = cats;
            loadable_categories_used = used;
            loadable_categories_allocated = allocated;
        } else {
            if (cats) free(cats);
            loadable_categories = nil;
            loadable_categories_used = 0;
            loadable_categories_allocated = 0;
        }
    
        if (PrintLoading) {
            if (loadable_categories_used != 0) {
                _objc_inform("LOAD: %d categories still waiting for +load\n",
                             loadable_categories_used);
            }
        }
    
        return new_categories_added;
    }
    

    相关文章

      网友评论

          本文标题:+load

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