iOS进阶笔记(七)7.1 +load和initialize的加载过程分析

📣 iOS进阶笔记目录

+load方法(不会触发消息机制)

相关runtime源码执行顺序

_objc_init -> load_images -> call_load_methods -> call_class_loads -> call_category_loads


调用时机:在runtime初始化动态链接器通知的注册,加载镜像时调用(先加载类,后加载分类)

// runtime初始化入口函数(objc-os.mm)
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();
    runtime_init();// 初始化runtime
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();

    // 动态链接器通知的注册
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

// objc-runtime-new.mm
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();
}

调用顺序

1、先调用类的+load方法,再调用分类的+load方法

在程序运行时,类和分类的+load方法只加载一次

// objc-loadmethod.mm
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;

// 引入autoreleasePool管理内存: do-while循环执行完毕时,会及时清理中间过程产生的临时变量以及内存资源
    void *pool = objc_autoreleasePoolPush();

    do {
        // 先调用类的+load方法
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads(); // 调用类的+load方法,该函数执行完毕后将loadable_classes_used置0,确保类的+load方法只被调用1次
        }
        // 再调用分类的+load方法
        // 2. Call category +loads ONCE
        more_categories = call_category_loads();// 该函执行完后,more_categories = false,确保分类的+load方法只调用1次

        // 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方法加载过程:

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;
        // 先加载父类方法,再加载子类方法
        // 获取该类方法的IMP 即load_method
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        // 通过IMP直接调用load函数(load_method为函数指针)
        (*load_method)(cls, @selector(load));
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

先调用父类,再调用子类的原因:

1)从load_images->prepare_load_methods-> schedule_class_load 可知,通过递归一直向上查找父类
2)父类在前,子类在后,依次将类和IMP存入loadable_class类型的结构体数组中。

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        // 内部通过递归加载,先调用父类load方法,再调用子类load方法
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    ASSERT(cls->isRealized());  // _read_images should realize

    // 确保只被调用一次
    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    // 递归一直往上找父类,确保父类先调用
    schedule_class_load(cls->getSuperclass());

    // 把类加载到数组中,父类在前,子类在后
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        // 动态扩容:若数组loadable_classes已满,使用realloc实现动态扩容
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    // 将类和IMP存入loadable_class类型的结构体数组中
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

调用类的+load方法小结:

  • 多个类先编译,先调用
  • 先调用父类的+load方法,再调用子类的+load方法
  • 直接获取类的方法的IMP,然后用load_method_t类型的函数指针直接调用类的load方法

2、调用分类的+load方法(多个分类按照编译的先后顺序调用)

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.
    // 遍历所有分类,通过IMP指针调用并执行load分类方法,最后再清空cats数组
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        // 获取分类方法的IMP
        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注册了
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            
           // 直接获取分类的方法的IMP,然后用load_method_t类型的函数指针直接调用分类load方法
            (*load_method)(cls, @selector(load));
            // cats数组遍历执行完调用load方法后,删除已加载的分类,清空cats数组
            cats[i].cat = nil;
        }
    }

    // Compact detached list (order-preserving)
    // 再次检查cats数组是否存在分类,并将存在分类的移到cats数组前面,最终得到只有分类的紧凑数组,节省空间。
    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++) {
        
        // 动态扩容:当分配的分类数组cats被占用完了,使用realloc实现动态扩容
        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.
    // 清空原有的数组loadable_categories
    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) {
        // 若存在新分类,则将当前cats分类数组及相关标志位更新
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        // 若不存在新分类加入,则释放cats数组,再将相关变量置为初始状态
        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方法小结:
  • 将全局分类数组loadable_categories赋值给了一个局部分类数组cats;

  • 遍历所有分类,通过IMP指针调用并执行load分类方法,最后再清空cats数组;

  • 再次检查cats数组是否存在分类,并将存在分类的移到cats数组前面,最终得到只有分类的紧凑数组,节省空间;

  • 若存在新的分类添加,则将其拷贝到紧凑数组中( 动态扩容:当分配的分类数组cats被占用完了,使用realloc实现动态扩容);

  • 清空原有的数组loadable_categories

  • 若存在新分类,则将当前cats分类数组及相关标志位更新;

  • 若不存在新分类加入,则释放cats数组,再将相关变量置为初始状态


类和分类的+load方法和自定义方法加载区别

加载自定义方法(setter方法等)通过isa基于objc_mgsSend()消息转发方式处理

  • 优先调用分类方法

  • 再调用类的方法

加载+load的方法直接找到类和分类的IMP,采用函数指针直接调用load方法

  • 优先调用类的+load方法
  • 再调用分类的+load方法

+initialize方法(会触发消息机制)

执行顺序为:lookUpImpOrForward -> realizeAndInitializeIfNeeded_locked -> initializeAndLeaveLocked -> initializeAndMaybeRelock -> initializeNonMetaClass -> callInitialize -> objc_msgSend(cls, @selector(initialize))


具体源码:

// 启动方法查找
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    // 调用汇编 STATIC_ENTRY __objc_msgForward_impcache
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;
    runtimeLock.assertUnlocked();

    if (slowpath(!cls->isInitialized())) {
        behavior |= LOOKUP_NOCACHE;
    }
    
    runtimeLock.lock();

    checkIsKnownClass(cls);

    cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
    // runtimeLock may have been dropped but is now locked again
    runtimeLock.assertLocked();
    curClass = cls;
    
    // 方法查找机制
    for (unsigned attempts = unreasonableClassCount();;) {
        
        // 一、当前类中查找
        if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
            // 1、先去当前类对象cache列表中查找是否有该方法
            // cache_getImp实现在 objc-msg-arm64.s 中STATIC_ENTRY _cache_getImp
            imp = cache_getImp(curClass, sel);
            if (imp) goto done_unlock;
            // 若没找到,则去父类中查找
            curClass = curClass->cache.preoptFallbackClass();
#endif
        } else {
            // 2、缓存中没有,再去当前类对象的方法列表(class_rw_t)中找
            // curClass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                // 获取方法实现,goto到done,将该方法条件到当前类的cache中
                imp = meth->imp(false);
                goto done;
            }
            // 若没找到方法实现且动态方法解析失败,则执行消息转发
            if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
                // No implementation found, and method resolver didn't help.
                // Use forwarding.
                //
                imp = forward_imp;
                break;
            }
        }

        // Halt if there is a cycle in the superclass chain.
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

        // 二、父类缓存查找
        // Superclass cache.
        imp = cache_getImp(curClass, sel);
        // 逐级向上层父类查找,若找到则停止查找,直接调用该方法,且不缓存
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but don't cache yet; call method
            // resolver for this class first.
            break;
        }
        if (fastpath(imp)) {
            // Found the method in a superclass. Cache it in this class.
            goto done;
        }
    }
    // 若消息发送失败,则启动动态方法解析
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }
 done:
    if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) {
#if CONFIG_USE_PREOPT_CACHES
        while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
            cls = cls->cache.preoptFallbackClass();
        }
#endif
        // 将查找到方法添加到cache
        log_and_fill_cache(cls, imp, sel, inst, curClass);
    }
 done_unlock:
    runtimeLock.unlock();
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    return imp;
}


static Class realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
    runtimeLock.assertLocked();
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }
    // 若没有被初始化过,则执行下面函数
    if (slowpath(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 cls;
}

// Locking: caller must hold runtimeLock; this may drop and re-acquire it
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)
{
    ......
    
     // 执行初始化程序,即objc_msgSend()
     callInitialize(cls);
     
     ......
}


void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}

+initialize方法小结:

  • 单纯引入一个类,并不会触发 +initialize 方法执行;只有当该类进行初始化(第一次消息发送消息objc_msgSend()),才会去执行。

调用顺序

  • 先调用父类的+initialize,再调用子类的+initialize (先初始化父类,再初始化子类,每个类只会初始化1次)

  • 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)

  • 若分类实现了+initialize,则会覆盖类本身的+initialize调用


以上(完)



posted @ 2021-08-04 20:57  ITRyan  阅读(142)  评论(0编辑  收藏  举报