在介绍之前,我们首先来了解一下类的使用,我们要使用一个类,大概要经过以下步骤

  1. 启动App,程序开始加载类到内存中(代码区)+(void)load
  2. 首次使用该类时,创建类对象(我们可以把它看作是一个单例,它在整个程序中只有一份)+(void)initialize
  3. 通过类对象创建实例对象+(instancetype)alloc、-(instancetype)init
  4. 通过实例对象,我们就可使用实例方法、类属性了

从上面的步骤我们也大概了解到load和initialize的调用时机了,下面在来详细说一下

一、+load类方法

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

  • 1.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类
  • 2.当一个类未实现load方法时,不会调用父类load方法
  • 3.类中的load方法执行顺序要优先于类别(Category)
  • 4.当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致)
  • 5.当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致

二、+initialize类方法

  • 即使类文件被引用进项目,但是没有使用,initialize不会被调用
  • 假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
  • 类或者其子类的第一个方法被调用前调用
  • 由于是系统自动调用,也不需要再调用 [super initialize],否则父类的initialize会被多次执行
  • 父类的initialize方法会比子类先执行
  • 子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法.
  • 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Categoryinitialize方法)

三、两者的异同

1、相同点

  1. loadinitialize会被自动调用,不能手动调用它们。
  2. 如果父类和子类都被调用,父类的调用一定在子类之前
  3. 子类实现了loadinitialize的话,会隐式调用父类的load和initialize方法。
  4. loadinitialize方法内部使用了锁,因此它们是线程安全的。

    2、不同点

  5. 调用顺序不同,以main函数为分界,+load方法在main函数之前执行,+initialize在main函数之后执行。

  6. 子类中没有实现+load方法的话,不会调用父类的+load方法;而子类如果没有实现+initialize方法的话,也会自动调用父类的+initialize方法。
  7. +load方法是在类被装在进来的时候就会调用,+initialize在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不会调用到+initialize方法。

    四、使用场景

    1.+load一般是用来交换方法Method Swizzle,由于它是线程安全的,而且一定会调用且只会调用一次,通常在使用UrlRouter的时候注册类的时候也在+load方法中注册。 ```objectivec +(void)load {

    static dispatch_once_t oneToken; dispatch_once(&oneToken, ^{

    1. Method imageNamed = class_getClassMethod(self,@selector(imageNamed:));

    Method mkeImageNamed =class_getClassMethod(self,@selector(swizze_imageNamed:)); method_exchangeImplementations(imageNamed, mkeImageNamed);

    }); }

  • (instancetype)swizze_imageNamed:(NSString*)name {

    //这个时候swizze_imageNamed已经和imageNamed交换imp,所以当你在调用swizze_imageNamed时候其实就是调用imageNamed UIImage * image; if( IS_IPHONE ){

    1. // iphone处理
    2. UIImage * image = [self swizze_imageNamed:name];
    3. if (image != nil) {
    4. return image;
    5. } else {
    6. return nil;
    7. }

    } else {

      // ipad处理,_ipad是自己定义,~ipad是系统自己自定义。
      UIImage *image = [self swizze_imageNamed:[NSString stringWithFormat:@"%@_ipad",name]];
      if (image != nil) {
          return image;
      }else {
          image = [self swizze_imageNamed:name];
          return image;
      }
    

    } ```

2.+initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值,或者说对一些静态常量进行初始化操作。

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

五、总结

  1. load和initialize方法都会在实例化对象之前调用
  2. load执行在main函数以前,initialize执行main函数之后
  3. 这两个方法会被自动调用,不能手动调用它们。
  4. load和initialize方法都不用显示的调用父类的方法而是自动调用
  5. 子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类
  6. initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。
  7. load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
  8. load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。