美文网首页
iOS - load 方法探索

iOS - load 方法探索

作者: felix6 | 来源:发表于2018-05-02 13:23 被阅读0次

[toc]

参考

load

initialize

http://www.jianshu.com/p/a358a397a4ce

http://blog.csdn.net/ShengQiangLiu/article/details/50866228

code

// NSObject 有实现该方法
- (void)load;

objc4源码解读

// objc-os.mm
_objc_init();

// objc-runtime-new.mm
load_images();
        // 准备(查找所有被实现的load方法)
        prepare_load_methods(); // Discover load methods
                /// 类 ★
                // 按(编译)顺序加载 classlist 数组中的类, 这个顺序就是 BuildPhases 中 CompileSources 的顺序
                classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
                schedule_class_load(remapClass(classlist[i])); // for循环
                        // ★★ 递归调用, 传入super, 所以父类早于子类添加到数组中, 保证父类早于子类load 
                        schedule_class_load(cls->superclass);
                        // ★★ 将cls添加到 loadable_classes 数组的最后面, call_class_loads()是从前往后遍历, 所以后添加的后加载; 
                        add_class_to_loadable_list(); 
                                method = cls->getLoadMethod(); // 获取 load 的IMP
                                if (!method) return; // ★★ 如果类没有实现 +load, 则不加入数组, 后面也不会去调用 
                                loadable_classes[loadable_classes_used].cls = cls;
                                loadable_classes[loadable_classes_used].method = method;
                /// 分类 ★
                // 按(编译)顺序加载 categorylist 数组中的分类
                category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
                // for循环, 将 cat 添加到 loadable_categories
                // 注意, 分类这里没有递归调用, 不用管 super
                add_category_to_loadable_list(); 
                        method = _category_getLoadMethod(cat);
                        if (!method) return;
                        loadable_categories[loadable_categories_used].cat = cat;
                        loadable_categories[loadable_categories_used].method = method;
        
        // 加载
        call_load_methods(); // objc-loadmethod.mm
                // 先调用所有类的load
                call_class_loads();
                        // 从上面prepare好的 loadable_classes 数组中取出所有类
                        struct loadable_class *classes = loadable_classes;
                        // for循环该数组, 取到类的load方法的内存地址, 赋值给函数指针load_method
                        load_method_t load_method = (load_method_t)classes[i].method;
                        // 使用函数指针, 直接调用每一个类的load方法
                        (*load_method)(cls, @selector(load));
                // 再调用所有分类的load
                call_category_loads();
                        load_method_t load_method = (load_method_t)cats[i].method;
                        (*load_method)(cls, @selector(load));
                        
结论★★:
  • 系统调用 +load 方法是根据方法地址直接调用, 并不是经过 objc_msgSend 函数调用。

    • 所以, 所有类的已实现的load 都会被调用 (未实现则不会调用, 也不会去调用父类的)。
    • 主类的 load 方法, 并不会被分类覆盖。
  • 先调用类的+load

    • 各个类之间, 按照编译先后顺序调用 (先编译,先调用)。
    • 调用子类的+ load之前会先调用父类的+ load
  • 再调用分类的+ load (分类的 load 是在所有主类 load 完毕之后才调用)。

    • 各个分类之间, 按照编译先后顺序调用 (先编译,先调用)。

调用时机:

+ load方法会在runtime加载类、分类时调用 [TBC ??? load 是 runtime 调用的吗]

程序一启动, 在main()函数执行之前, 当类或分类被加载到内存时被调用。

换句话说, 这个load方法在 didFinishLaunchingWithOptions 之前就被调用了;

《Apple Document》
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

调用次数:

每个类、分类的+ load,在程序运行过程中, 默认会且只会执行一次

调用必然性:

必然调用, 不管程序运行过程中有没有用到这个类, 都会调用load方法 (如果有实现)

调用顺序:

image

同一继承体系下, 先加载父类, 再加载子类, 然后再加载子类的分类(按编译顺序, 先编译先调用);
不同的类之间的加载顺序: 是不确定的 按照编译先后顺序调用(先编译, 先调用)。

《Apple Document》
A class’s +load method is called after all of its superclasses’ +load methods.
A category +load method is called after the class’s own +load method.

见【objc4源码解读 - 结论】

父类

父类先于子类加载, 子类不要手动调用 [super load] , 否则父类的load会执行多次。

load 不遵循继承规则, 不管子类有没有写load方法, 都不会去查找调用父类的load

分类

与其他方法不同, 每个类的load都是独立的, 不存在继承、重写, 在Category中重写load函数不会替换原始类中的load, 原始类和Category中的load函数都会被执行, 原始类的load会先被执行, 再执行Category中的load函数。

当有多个 Category 都实现了load函数, 这几个load函数都会执行, 按编译顺序, 先编译先调用。

调用方式:

系统自动调用, 不要手动调用 (但实际也能调用)

安全性

线程安全 内部加锁 线程阻塞

在load方法中使用其他类是不安全的, 因为会调用其他类的load方法, 而如果关系复杂的话, 就无法判断出各个类的载入顺序, 类只有初始化完成后, 类实例才能进行正常使用

尽可能的精简load方法, 因为整个应用程序在执行load方法时会阻塞, 即, 程序会阻塞直到所有类的load方法执行完毕, 才会继续

应用场景

一般的应用场景是在该方法中实现方法交换(Method Swizzling)

面试题

Category中有load方法吗?load方法是什么时候调用的?

有load方法

load方法在runtime加载类、分类的时候调用 【TBC】

load 方法能继承吗?★★
  • 答案

    load 方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用。

  • 验证:

    先创建继承自 NSObject 的 QGPerson, 实现 +load 方法, 添加打印代码;

    然后创建继承自 QGPerson 的 QGStudent, 不实现 +load 方法;

    然后在 main() 函数中, 手动调用 [QGStudent load];

    会发现 QGPerson+loadmain() 前后被调用了2次。

    可见 +load 是存在继承的, 如果自己没有实现, 可以沿着super_class调用父类的。

  • 解析:

    首先, [QGStudent load]; 这样写就是消息发送机制, 相当于 objc_msgSend([QGStudent class], @selector(load));

    会根据 QGStudent 的 isa 找到其元类对象, 在其元类对象中查找 load, 找不到, 再根据 super_class 找到父元类对象, 从而找到并调用了父类的 +load。

load、initialize的区别?
  • 调用方式

    load 是根据函数地址直接调用

    initialize 是通过 objc_msgSend 调用

  • 调用时刻

    load 是 runtime 加载类、分类的时候调用(只会调用1次)

    initialize 是类第一次接收到消息的时候调用, 每一个类只会 initialize 一次(父类的initialize方法可能会被调用多次)

load、initialize 的调用顺序?
  • load

    【见本文- 源码 - 结论】

  • initialize

    先初始化父类

    再初始化子类(可能最终调用的是父类的initialize方法)

load、initialize 在category中的调用的顺序?
load、initialize 出现继承时他们之间的调用过程?
系统是怎么调用 load 方法的?

不是通过消息机制, 而是直接通过函数指针调用

相关文章

网友评论

      本文标题:iOS - load 方法探索

      本文链接:https://www.haomeiwen.com/subject/mnxcrftx.html