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

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

相关文章
|
15天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
51 2
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
137 3
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
3月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
48 1
|
3月前
|
开发框架 Android开发 Swift
安卓与iOS应用开发对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。本文将深入探讨这两大操作系统在开发环境、编程语言、用户界面设计、性能优化及市场分布等方面的差异和特点。通过比较分析,旨在为开发者提供一个宏观的视角,帮助他们根据项目需求和目标受众选择最合适的开发平台。同时,文章还将讨论跨平台开发框架的利与弊,以及它们如何影响着移动应用的开发趋势。
|
3月前
|
安全 搜索推荐 Android开发
安卓与iOS应用开发的对比分析
【8月更文挑战第20天】在移动应用开发领域,安卓和iOS两大平台各领风骚。本文通过深入探讨两者的开发环境、编程语言、用户界面设计、应用市场及分发机制等方面的差异,揭示了各自的优势和挑战。旨在为开发者提供决策支持,同时帮助理解为何某些应用可能优先选择在一个平台上发布。
46 2
|
3月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的大舞台上,安卓与iOS两大操作系统各占半壁江山。本文将深入浅出地比较两者的开发环境,从开发工具、编程语言到用户界面设计等多个维度进行剖析,旨在为初入行的开发者们提供一盏明灯,帮助他们选择适合自己的开发路径。通过实例分析和数据支持,我们将揭示这两个平台的独特优势和潜在挑战,以及它们如何影响应用的性能和用户体验。
69 1