iOS - load 方法探索

[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 方法的?

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容