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(分类的方法追加到了方法列表的前面,所以会优先调用);

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

相关文章
|
3天前
|
安全 Android开发 iOS开发
探索Android与iOS开发的差异:平台特性与用户体验的对比分析
在移动应用开发的广阔天地中,Android和iOS两大阵营各据一方。本文将深入探讨这两个操作系统在开发环境、编程语言、用户界面设计及市场分布等方面的主要区别。通过比较分析,我们将揭示各自平台的特有优势,并讨论如何根据目标受众和业务需求选择适合的开发平台。
|
6天前
|
搜索推荐 安全 Android开发
安卓与iOS操作系统的对比分析
在移动设备市场上,安卓和iOS操作系统一直是主要竞争对手。本文将从用户界面、应用生态系统、定制化程度和安全性等方面对安卓和iOS进行对比分析,并探讨两者在不同场景下的适用性。
|
6天前
|
Java Android开发 iOS开发
深入探讨移动操作系统的性能优化:安卓与iOS的对比分析
在现代移动设备中,操作系统的性能优化至关重要。本文从系统架构、内存管理、电池续航和应用程序运行效率等多个维度,深入探讨了安卓(Android)和iOS两大主流移动操作系统的优化策略及其实际效果,旨在为开发者和用户提供更清晰的了解和选择依据。
17 0
|
12天前
|
安全 Android开发 iOS开发
安卓与iOS操作系统的比较分析
【6月更文挑战第5天】本文将深入探讨安卓和iOS两大主流操作系统的特点、优势和劣势。通过对比分析,我们将揭示这两个系统在性能、安全性、用户体验等方面的差异,帮助用户更好地了解这两个系统,从而做出更明智的选择。
|
20天前
|
人工智能 vr&ar Android开发
安卓与iOS系统的发展趋势及影响分析
在移动互联网时代,安卓和iOS作为两大主流移动操作系统,在不断发展变化中展现出不同的特点和发展趋势。本文从技术性角度出发,分析了安卓和iOS系统的发展趋势,并探讨了它们对移动设备市场和用户体验的影响,帮助读者更好地理解当前移动操作系统的发展方向和未来可能的变化。
18 0
|
1月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
45 1
|
1月前
|
存储 运维 安全
iOS加固原理与常见措施:保护移动应用程序安全的利器
iOS加固原理与常见措施:保护移动应用程序安全的利器
40 0
|
1月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
58 4
|
1月前
|
安全 前端开发 数据安全/隐私保护
【教程】 iOS混淆加固原理篇
本文介绍了iOS应用程序混淆加固的缘由,编译过程以及常见的加固类型和逆向工具。详细讨论了字符串混淆、类名、方法名混淆、程序结构混淆加密等加固类型,并介绍了常见的逆向工具和代码虚拟化技术。
|
1月前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。