09-iOS之load和initialize底层调用原理分析

简介: 09-iOS之load和initialize底层调用原理分析

load方法

测试代码:新建Person类,在里面添加断点:

#import "Person.h"
@implementation Person
+ (void)load {
    NSLog(@"load");
}
@end

调用顺序如图:

接下来通过源码分析一下底层调用;

load_images函数

首先看断点3处的load_images中函数:

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)
    call_load_methods();
}

可以看到内部调用了prepare_load_methods函数,准备需要调用的所有load函数方法,接下来看一下prepare_load_methods函数;prepare_load_methods函数

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++) {
        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);
    }
}

可以看到schedule_class_load函数内部首先被调用了,接下来调用了add_category_to_loadable_list,将分类的load插入表中;

schedule_class_load函数

接下来查看schedule_class_load的内部实现,可以发现通过递归先查找父类里面的load,通过add_class_to_loadable_list函数插入表中,再查找当前类的load,插入表中:

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);
}

loadable_classes是一个全局的结构体, add_class_to_loadable_list作用是将模块里所有类的load函数存放到loadable_classes中,并且放到末尾;

add_class_to_loadable_list函数接下来看add_class_to_loadable_list函数:

void add_class_to_loadable_list(Class cls)
{
    ...
    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++;
}

add_category_to_loadable_list函数add_category_to_loadable_list同理,将所有分类的load放到分类表中,先编译的会先加入,后编译的后加入:

void add_category_to_loadable_list(Category cat)
{
    ...
    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++;
}

call_load_methods函数

接下来调用call_load_methods,内部通过do-while循环可以看到优先通过call_class_loads函数调用类的load函数,接着通过call_category_loads函数调用分类的函数,故类的load会先与分类的load被调用:

void call_load_methods(void)
{
    ...
    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函数

从表中的索引0开始依次取出load函数进行调用, 先编译的类会优先被调用,内部通过(*load_method)(cls, @selector(load))函数调用load函数,是通过C语言函数调用的流程,不是OC的给对象发送消息,所以不会走消息发送流程,也就是说如果一个类实现了load函数就会调用,如果没有实现load方法,也就不存在说调用该类的父类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;
        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, @selector(load));
    }
    // Destroy the detached list.
    if (classes) free(classes);
}

分类调用探究

为Person增加分类,新建Person+Cate文件

@implementation Person (Cate)
+ (void)load {
    NSLog(@"load");
}


@end

通过断点调试,调用顺序如图:

call_category_loads函数

前面的与上面一致,只看call_category_loads调用分类的函数,从表中的索引0开始依次取出load函数进行调用, 先编译进入的分类会优先被调用:

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()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n",
                             cls->nameForLogging(),
                             _category_getName(cat));
            }
            (*load_method)(cls, @selector(load));
            cats[i].cat = nil;
        }
    }
    ...
}

initialize方法

当类第一次接收到消息的时候initialize会被调用;

测试代码如下:

@implementation Person
+ (void)initialize {
    NSLog(@"Person - initialize");
}


@end

首次调用:

Person *p = [Person new]

__objc_msgSend_uncached函数

首先进入__objc_msgSend_uncached,接下来调用lookUpImpOrForward:

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    ...
    cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
    ...
}

realizeAndInitializeIfNeeded_locked函数

接下来调用realizeAndInitializeIfNeeded_locked:

static Class
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
    ...
    if (slowpath(initialize && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
    }
    return cls;
}

initializeAndLeaveLocked函数

接下来调用initializeAndLeaveLocked函数:

static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
    return initializeAndMaybeRelock(cls, obj, lock, true);
}

initializeAndMaybeRelock函数

接下来调用initializeAndMaybeRelock函数:

initializeNonMetaClass函数

static Class initializeAndMaybeRelock(Class cls, id inst,
                                      mutex_t& lock, bool leaveLocked)
{
    ...
    initializeNonMetaClass(nonmeta);
}

接下来调用initializeNonMetaClass,可以看出如果存在父类,并且父类如果没有调用,则优先调用父类,如果已经调用initialize,下次父类不会再调用initialize:

void initializeNonMetaClass(Class cls)
{
    supercls = cls->getSuperclass();
    if (supercls && !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
    ...
    {
        callInitialize(cls);
    }
}

callInitialize函数

接下来调用callInitialize:

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

可以看到callInitialize中initialize函数的调用是通过objc_msgSend消息发送机制,由OC的消息机制可知,如果子类没有实现initialize,父类实现了,则父类的可能会被调用多次initialize函数;

总结

load方法的调用时机?

  • load方法会在runtime加载类、分类时调用,main函数之前(通过C函数调用);
  • 每个类、分类的load,在程序运行过程中只调用一次;
  • 父类 > 子类 > 分类
    同一级别按照编译先后顺序调用(先编译,先调用)

initialize方法的调用时机?

  • initialize方法会在类第一次接收到消息时调用(通过objc_msgSend调用);
  • 先调用父类的initialize,再调用子类的initialize(先初始化父类,再初始化子类,每个类只会初始化1次);
  • 如果子类没有实现initialize,会调用父类的initialize,所以父类的+initialize可能会被调用多次;
  • 如果分类实现了initialize,会优先调用分类的initialize(分类的方法追加到了方法列表的前面,所以会优先调用);

本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

相关文章
|
21天前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS无疑是两大主角。它们各自拥有独特的开发环境、工具集和生态系统。本文深入探讨了这两个平台的开发环境,从编程语言、IDE到模拟器的使用,再到市场覆盖度和用户群体差异。通过比较,旨在为开发者提供选择合适平台的参考依据,并引发对如何优化跨平台开发策略的进一步思考。
25 0
|
3天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的大舞台上,安卓与iOS两大操作系统各占半壁江山。本文将深入浅出地比较两者的开发环境,从开发工具、编程语言到用户界面设计等多个维度进行剖析,旨在为初入行的开发者们提供一盏明灯,帮助他们选择适合自己的开发路径。通过实例分析和数据支持,我们将揭示这两个平台的独特优势和潜在挑战,以及它们如何影响应用的性能和用户体验。
11 1
|
12天前
|
开发框架 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第7天】在移动应用开发的广阔天地中,安卓和iOS两大平台各据一方。本文将深入探讨这两个平台的异同,从开发环境、工具支持到市场定位等方面进行比较。我们将不涉及具体的代码示例,而是聚焦于开发生态的宏观视角,为即将踏入这一领域的开发者提供一个全面的视角。
41 6
|
13天前
|
Android开发 Swift iOS开发
安卓与iOS开发环境的差异性分析
【8月更文挑战第6天】在移动应用开发的广阔天地中,安卓和iOS两大平台各自占据半壁江山。本文将深入探讨这两个操作系统在开发环境方面的主要差异,从编程语言、工具框架到开发成本等多个维度进行比较。通过分析,旨在为开发者提供一个清晰的平台选择指导,帮助他们根据项目需求和个人偏好做出更明智的决策。
41 5
|
11天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓与iOS两大平台各占半壁江山。本文通过浅显的语言和直观的比喻,探讨了这两大操作系统在开发环境上的差异与特点,旨在为初入行的开发者们提供一个清晰的指南。我们将从开发工具、编程语言、用户界面设计以及生态系统四个方面进行比较,帮助读者理解每个平台的优势与局限。
|
12天前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的大潮中,安卓和iOS两大平台各自展现出独特的开发环境和生态系统。本文将从开发者的角度出发,深入探讨这两个平台在编程语言、开发工具、用户界面设计以及市场分布等方面的不同特点。通过比较分析,旨在为移动应用开发者提供一份实用的参考,帮助他们在项目初期做出更加明智的平台选择。
|
12天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS平台如同双子星座般耀眼。本文将深入探讨这两大操作系统的开发环境,从语言、工具到生态系统,揭示它们的独特魅力与挑战。通过比较,我们不仅能更清晰地看到各自的特点,还能理解开发者如何在这两个平台上进行高效工作。无论你是安卓的忠实拥趸还是iOS的狂热粉丝,这篇文章都将为你提供一个全面的视角,让你对移动应用开发有更深的认识。
23 1
|
14天前
|
Java 开发工具 Android开发
安卓与iOS开发环境的差异性分析
在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文深入探讨了这两个平台在开发环境上的主要差异,从编程语言、开发工具到用户界面设计等多个维度进行比较。旨在为开发者提供一个清晰的指南,帮助他们根据项目需求和个人偏好做出明智的选择。通过实际案例分析,揭示各自平台的独有优势和可能面临的挑战,从而让读者能够更加全面地理解两大系统在移动开发领域的竞争与合作。
28 3
|
14天前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,Android和iOS两大操作系统各据一方,它们各自拥有独特的开发环境。本文将深入探讨这两种平台的开发环境,从编程语言、工具、集成开发环境(IDE)到用户界面设计等方面进行比较。我们旨在揭示两者的异同点,并试图理解这些差异如何影响开发者的选择和应用的性能。通过实例分析,我们将探索如何在不同的环境下优化开发流程,以期为开发者提供实用的指导。
|
15天前
|
移动开发 开发工具 Android开发
安卓与iOS开发环境对比分析
在移动应用开发的广阔天地中,安卓和iOS是两大主导力量。它们各自拥有独特的开发环境和工具,这些差异深刻影响着开发者的决策与应用的性能。本文将深入探讨这两个平台的开发环境,通过比较它们的编程语言、开发工具、用户界面设计及生态系统支持,揭示它们各自的优势与局限。我们旨在为即将踏入移动开发领域的新手提供一个清晰的指南,并激发对移动开发更深层次探索的兴趣。