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

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

相关文章
|
1月前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
22 1
|
1月前
|
存储 运维 安全
iOS加固原理与常见措施:保护移动应用程序安全的利器
iOS加固原理与常见措施:保护移动应用程序安全的利器
28 0
|
2月前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
38 4
|
3月前
|
存储 运维 安全
iOS加固原理与常见措施:保护移动应用程序安全的利器
iOS加固原理与常见措施:保护移动应用程序安全的利器
39 0
|
2月前
|
安全 前端开发 数据安全/隐私保护
【教程】 iOS混淆加固原理篇
本文介绍了iOS应用程序混淆加固的缘由,编译过程以及常见的加固类型和逆向工具。详细讨论了字符串混淆、类名、方法名混淆、程序结构混淆加密等加固类型,并介绍了常见的逆向工具和代码虚拟化技术。
|
2月前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。
|
3月前
|
安全 算法 前端开发
【完整版教程】iOS混淆加固原理篇
在iOS开发中,应用程序的安全性和保护显得尤为重要。由于iOS系统的开放性,一些逆向工具可以轻松地对应用程序进行反编译和分析,从而导致应用程序源代码、算法和敏感信息的泄露。为了保护应用程序的安全性,我们需要对应用程序进行混淆加固。本文将介绍iOS混淆加固的原理和常见的加固类型。
|
3月前
|
JSON 安全 数据安全/隐私保护
​iOS Class Guard github用法、工作原理和安装详解及使用经验总结
​iOS Class Guard github用法、工作原理和安装详解及使用经验总结
17 0
|
3月前
|
安全 数据安全/隐私保护 iOS开发
【iOS开发】iOS App的加固保护原理:使用ipaguard混淆加固
【iOS开发】iOS App的加固保护原理:使用ipaguard混淆加固
48 0
|
4月前
|
JSON 安全 数据安全/隐私保护
​iOS Class Guard github用法、工作原理和安装详解及使用经验总结
iOS Class Guard是一个用于OC类、协议、属性和方法名混淆的命令行工具。它是class-dump的扩展。这个工具会生成一个symbol table,这个table在编译期间会包含进工程中。iOS-Class-Guard能有效的隐藏绝大多数的类、协议、方法、属性和 实例变量 名。iOS-Class-Guard不是应用安全的最终解决方案,但是它绝对能让攻击者更难读懂你的程序。iOS-Class-Guard会加大代码分析和runtime检查的难度,这个工具可以认为是一个简单基础的混淆方法。由于OC的架构决定了iOS应用程序的剖析相当简单,check out一下链接就知晓了: