iOS底层原理之load_images分析

1. load_images的调用时机

  • 在objc_init中,进行完一系列配置初始化完成之后
  • 调用_dyld_objc_notify_register方法
  • map_images调用
  • load_images调用
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);
}

2. 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);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

2.1 准备阶段

-首先,使用prepare_load_methods方法去发现准备非懒加载的类和分类

  • 懒加载的类在编译阶段已经确定了,所以只需要获取非懒加载类列表
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    // 获取非懒加载的类列表
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }
     // 获取非懒加载的分类列表
    category_t **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);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}
2.1.1 非懒加载类的处理
  • 1._getObjc2NonlazyClassList获取类列表
  • 2.遍历类列表,使用schedule_class_load对类进行处理
// 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->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
  • 3.使用add_class_to_loadable_list添加类到已经加载的类表中去
/***********************************************************************
* add_class_to_loadable_list
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
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());
    }
    
    // loadable_list创建以及扩容,采用2倍扩容
    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_list开始存储当前类cls以及method
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

  • 4.使用method = cls->getLoadMethod()来获取当前类的load方法
  • 5.使用loadable_class结构体,来存储当前类cls和当前类的load方法,并且用一个数组loadable_classes来存放整体的非懒加载类的列表
struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};
2.1.2 非懒加载分类的处理
  • 1._getObjc2NonlazyCategoryList获取分类列表
  • 2.遍历分类列表,使用add_category_to_loadable_list把列表存储到另外一个loadable_list中loadable_categories
  • 3.内部原理和上面存储类一样,
// 具体存储方法
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;

// 该结构体存储分类cat以及method
struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};
2.1.3 小结
  • 准备阶段到此结束,发现并且遍历处理,使用两个数组loadable_classes以及loadable_categories存放所有的非懒加载类和分类

2.2 调用阶段

  • call_load_methods方法,方法内部使用一个do-while循环来保证类和分类的load方法能够全部被调用,loadable_classes_used>0代表存在着多个非懒加载类,那么就有多个类的load方法已经被开发者自己实现了
  • load方法 就是在这里调用的
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);
  • call_class_loads,类的load方法进行调用
image.png
  • call_category_loads,分类load方法的调用
image.png
  • 区别,类的所有分类的load方法调用完成之后,会一个个依次销毁
    // 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;
    }

2.3 load_imags结束

  • 至此,load_images调用结束,_objc_init也调用结束
  • 也代表整个dyld阶段结束

3 常见问题

3.1当分类和类有同样的方法时,如何调用
  • 如果是load方法,那么主类先调用,分类后调用;
  • 如果是其他方法,那么则只有分类的同名方法会调用(会产生类似覆盖的假象,但实际上只是分类方法插入到了主类的方法列表头部产生的)
3.2 initialize调用
  • initialize会在类首次进行消息发送时被调用,可以被子类重写
  • lookUpImpOrForward->initializeAndLeaveLocked->initializeAndMaybeRelock->initializeNonMetaClass->callInitialize(cls)
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
  • 最后,来张简单的流程图镇楼~
load_images流程图.png

你可能感兴趣的:(iOS底层原理之load_images分析)