Objective-C的+load方法调用原理分析

Objective-C之Category的底层实现原理
Objective-C的+initialize方法调用原理分析


别人是这么说的
  • 调用时机:+load方法会在Runtime加载类对象(class)和分类(category)的时候调用
  • 调用频率: 每个类对象、分类的+load方法,在工程的整个生命周期中只调用一次
  • 调用顺序:

    先调用类对象(class)的+load方法:

    • 类对象的load调用顺序是按照 类文件的编译顺序 进行先后调用;
    • 调用子类+load之前会先调用父类的+load方法

    再调用分类(category)的+load方法:按照编译先后顺序调用(先编译的,先被调用)

一、load方法的调用时机和调用频率

+load方法是在程序一启动运行,加载镜像中的类对象(class)和分类(category)的时候就会调用,只会调用一次,不论在项目中有没有用到该类对象或者该分类,他们统统都会先被加载进内存,因为类的加载只有一次,所以所有的load方法肯定都会被调用而且只有一次。下面先上一个小demo调试看看:
image.png
CLPerson
CLPerson+Test
CLPerson_Test2
main.m

上图里面,我创建了一个person类,以及它的两个分类–CLPerson+Test/CLPerson+Test2,然后给它们都加上两个类方法(+load/+test),main.h里面先不加任何代码跑跑看。
运行日志

从日志看出,虽然整个工程都没有import过CLPerson以及它的两个分类,但是他们的load方法还是被调用了,并且都发生在main函数开始之前,而且+test并没有被调用。所以该现象间接证明了,load方法的调用应该和类对象以及分类的加载有关。

main.h里面调一下+test方法
调用一下+test方法

+test方法只调用了一次,并且调用的是分类的+test方法,这就是所谓的“覆盖”现象

接下来通过源码分析一下(Runtime源码下载地址)

首先,进入Runtime的初始化文件objc-os.mm,找到_objc_init函数,该函数可以看作是Runtime的初始化函数。

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
   
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

忽略一些与本文主题关联不太的函数,直接看最后句_dyld_objc_notify_register(&map_images, load_images, unmap_image);其中很明显,load_images就是加载镜像/加载模块的意思,应该是与我们话题相关的参数,点进去看看它的实现

/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);

void
load_images(const char *path __unused, const struct mach_header *mh)
{
   
    // 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();
}

苹果对该函数官方给出的注释是,处理那些正在进行映射的镜像(images)的+load方法。该方法的实现里面,做了两件事情:

  • prepare_load_methods// Discover load methods – 查找并准备load方法,以供后面去调用
  • call_load_methods();//Call +load methods – 调用这些load方法

针对上面案例日志中出现的现象,先从结果出发,逆向分析,来看看load方法是如何调用的,进入call_load_methods();的实现

/***********************************************************************
* call_load_methods

* Call all pending class and category +load methods.
调用所有的处理中的class和category的+load方法;

* Class +load methods are called superclass-first. 
class的+load方法会被先调用,并且,一个调用一个class的+load方法前,会先对其父类的+load进行调用

* Category +load methods are not called until after the parent class's +load.
category的+load方法的调用,会发生在所有的class的+load方法完成调用之后。
* 
* Th
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值