iOS +load与+initialize

一、load

Apple文档中这样描述,传送门

在这里插入图片描述

1、load源码解析

调用方式

load方法调用顺序
类load调用源码
load方法的调用源码
load的方法中直接拿到load方法的内存地址直接调用方法。

分类调用源码
分类load方法的调用源码
分类中也是通过直接拿到load方法的地址进行调用。因此正如我们之前试验的一样,分类中重写load方法,并不会优先调用分类的load方法,而不调用本类中的load方法了。

2、 load函数调用特点

当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次;由于load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。

  • ①.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类;
  • ②.当子类未实现load方法时,不会调用父类的load方法;(注:此处跟父类load执行无关,意思是子类没有实现load,父类的load不会执行两次)
  • ③.类中的load方法执行顺序要优先于类别(Category);
  • ④.当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序和类别在Compile Sources中出现的顺序一致)
  • ⑤.当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致。

3、验证

新建两个类:Person和Child,Person继承NSObject,Child继承Person

Person : NSObject
Child : Person

新建两个分类:Person (Test)和Child (Test)
在这里插入图片描述

Person (Test)
Child (Test)

load的运行结果

load——Person
load——Child
load——Child (Test)
load——Person (Test)

二、initialize

Apple文档中这样描述,传送门
在这里插入图片描述

1、initialize源码解析

我们知道当类第一次接收到消息时,就会调用initialize,相当于第一次使用类的时候就会调用initialize方法。调用子类的initialize之前,会先保证调用父类的initialize方法,如果之前已经调用过initialize,就不会再调用initialize方法了。当分类重写initialize方法时会先调用分类的方法。
在这里插入图片描述
上图中我们发现,initialize是通过消息发送机制调用的,消息发送机制通过isa指针找到对应的方法与实现,因此先找到分类方法中的实现,会优先调用分类方法中的实现。

2、 initialize函数调用特点

initialize在类或者子类的第一个方法被调用前调用。即使类文件被引用进项目,但是没有使用,initialize也不会被调用。由于是系统自动调用,也不需要再调用[super initialize],否则父类的initialize会被多次执行。假如这个类放到代码中,而这段代码并没有执行,这个函数是不会执行的。

  • ①.父类的initialize方法会比子类先执行;
  • ②.当子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法;
  • ③.当有多个Category都实现了initialize方法,会覆盖类种的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)

三、load和initialize使用场景

load

由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在load方法中

load很常见的一个使用场景,交换两个方法的实现

//摘自MJRefresh
+ (void)load
{
    [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
    [self exchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)];
    [self exchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];
}

+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
{
    method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
}

initialize

initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值。

比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:

// In Person.m
// int类型可以在编译期赋值
static int someNumber = 0; 
static NSMutableArray *someArray;
+ (void)initialize {
    if (self == [Person class]) {
        // 不方便编译期复制的对象在这里赋值
        someArray = [[NSMutableArray alloc] init];
    }
}

四、总结

  • load和initialize方法都会在实例化对象之前调用

  • load执行在main函数以前,initialize执行main函数之后

  • 这两个方法会被自动调用,不能手动调用它们。

  • load和initialize方法都不用显示的调用父类的方法而是自动调用

  • 子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类

  • initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。

  • load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。

  • load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxiaobukuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值