专栏首页ShaoYLiOS Category实现原理 (补充)

iOS Category实现原理 (补充)

iOS Category实现原理 (补充)

load 和 initialize

load

  1. load方法会在程序启动就会调用,当装载类信息的时候就会调用。
    • 调用顺序看一下源代码。在 objc-loadmethod.m 文件中实现

    void call_load_methods(void) { static bool loading = NO; bool more_categories; loadMethodLock.assertLocked(); // Re-entrant calls do nothing; the outermost call will finish the job. if (loading) return; loading = YES; void *pool = objc_autoreleasePoolPush(); do { // 1. Repeatedly call class +loads until there aren't any more // 1.调用类的 load 方法 while (loadable_classes_used > 0) { call_class_loads(); } // 2. Call category +loads ONCE // 2.调用分类的 load 方法 more_categories = call_category_loads(); // 3. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop(pool); loading = NO; }

  2. 通过源码我们发现是优先调用类的load方法,之后调用分类的load方法。
  3. 查看load方法的调用源码,在 objc-loadmethod.m 文件中
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, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}
  1. 我们看到load方法中直接拿到load方法的内存地址直接调用方法,不在是通过消息发送机制调用。
  2. 所以原始类的load方法并不会被覆盖,且调用类的load方法之前会保证其父类已经调用过load方法。

initialize

  1. 当类第一次接收到消息时,就会调用initialize,相当于第一次使用类的时候就会调用initialize方法。调用子类的initialize之前,会先保证调用父类的initialize方法。如果之前已经调用过initialize,就不会再调用initialize方法了。当分类重写initialize方法时会先调用分类的方法。
    • 看一下initialize的源码

    void callInitialize(Class cls) { ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize); asm(""); }

  2. 由此我们发现,initialize是通过消息发送机制调用的,消息发送机制通过isa指针找到对应的方法与实现,因此先找到分类方法中的实现,会优先调用分类方法中的实现。

总结

  1. Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
    • Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法。
  2. load、initialize的区别,以及它们在category重写的时候的调用的次序。
  • 调用方式:
    • load是根据函数地址直接调用,initialize是通过objc_msgSend调用
  • 调用时刻:
    • load是runtime加载类、分类的时候调用(只会调用1次),
    • initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)
  • 调用顺序:
    • 先调用类的load方法,先编译那个类,就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法,先编译的分类优先调用load方法。
    • initialize先初始化父类,之后再初始化子类。如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),如果分类实现了+initialize,就覆盖类本身的+initialize调用。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iOS:swift :可选类型

    用户1941540
  • 深刻理解----修饰变量----关键字

    用户1941540
  • iOS 声明属性关键字讲解

    用户1941540
  • OC - load 和 initialize

    Person 以及它的两个分类 Person (Test)、Person (Test2) 都实现了+test和+load两个方法,且 Person (Test2...

    师大小海腾
  • runtime的那些事(三)——NSObject初始化 load 与 initialize

     作为iOS开发,多少都与 load 方法打过交道——在程序 main 函数调用前,类被注册加载到内存时,load 方法会被调用。也就是说每个类的 load 方...

    我只不过是出来写写代码
  • 类方法load和initialize的区别

    Objective-C作为一门面向对象语言,有类和对象的概念。编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用。在应用程序运行起来的时候,类的...

    用户3539187
  • ARM架构学习

    ARM处理器是英国Acorn有限公司设计的低功耗成本的第一款RISC微处理器。全称为Advanced RISC Machine。

    李小白是一只喵
  • 嵌入式Linux系统在线升级策略

    由于市面上大多数嵌入式设备的分散、数量庞大、部署地点情况复杂,因此对于这些设备进行个体、本地升级的实施非常费时费力。针对这种现状,本文提供一种对基于 Linux...

    企鹅号小编
  • 如何使用 Vultr Snapshots 创建快照功能

    魏艾斯博客www.vpsss.net
  • 互联网技术人才招聘分析报告——什么语言的程序员最受CEO青睐?

    互联网技术人才招聘分析报告——什么语言的程序员最受CEO青睐? ? 对每个创业CEO而言,都会花大量的时间寻找支撑业务发展的优秀人才,那到底什么语言的程序员“最...

    用户1667431

扫码关注云+社区

领取腾讯云代金券