iOS之load与initialize详解和区别

前言

在iOS开发load和initialize都接触过,对于他们的区别,大多数开发者可能不是特别清楚。所以下面就详解一下load与initialize

load类方法

load类方法特点:

  • 当类被导入到项目的时候就会执行load函数,
  • 在main函数开始执行之前的,这个类是否被用到无关
  • 每个类的load函数只会自动调用一次
  • load函数是系统自动加载的,不需要调用父类的load函数

load类方法调用的特点:

  • 父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类。

创建两个类Person,Student。 Student继承Person

Person : NSObject
Student : Person

在Person.m、Student.m文件中实现load方法

//Person
@implementation Person

+ (void)load {
    
    NSLog(@"%s",__FUNCTION__);
}
@end

//Student
@implementation Student

+ (void)load {
    
    NSLog(@"%s",__FUNCTION__);
}
@end

然后运行代码:

[Person load]
[Student load]

执行结果可以得出父类的load方法执行顺序要优先于子类

  • 子类未实现load方法时,不会调用父类load方法

创建一个Teacher继承与Person,创建完成,不去实现load方法,运行项目:

[Person load]
[Student load]

运行结果说明:没有实现load方法,不去调用父类load方法

  • 类中的load方法执行顺序要优先于类别(Category)

创建3个对象Person (Category)、Person (Category2)、Person (Category3),并且分别实现load方法

//Person+Category.m
+(void)load {

    NSLog(@"%s",__FUNCTION__);
}

//Person+Category2.m
+(void)load {

    NSLog(@"%s",__FUNCTION__);
}

//Person+Category3.m
+(void)load {

    NSLog(@"%s",__FUNCTION__);
}

在运行项目执行结果如下:

+[Person load]
+[Student load]
+[Person(Category1) load]
+[Person(Category3) load]
+[Person(Category2) load]

执行结果表明,类中的load方法执行顺序要优先于类别(Category)

  • 有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致)

查看一下Compile Sources的文件顺序,如下图:

Compile_Sources.png

执行顺序和Compile Sources顺序是一样的。

  • 有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致

前面Teacher类 实现也load方法,如下:

//Teacher.h
+(void)load {

    NSLog(@"%s",__FUNCTION__);
}

运行一下项目,执行结果:

[Person load]
[Teacher load]
[Student load]
[NSObject(Category2) load]
[NSObject(Category) load]
[NSObject(Category3) load]

在Compile Sources出现的顺序,Teacher在Student以前,Teacher与Student也不是继承关系,不同类执行顺序和Compile Sources一至。

initialize

initialize类方法的特点:

  • 即使类文件被引用进项目,但是没有使用,initialize不会被调用

  • 假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。

  • 类或者其子类的第一个方法被调用前调用

  • 由于是系统自动调用,也不需要再调用 [super initialize] ,否则父类的initialize会被多次执行

initialize类方法调用的特点:

  • 父类的initialize方法会比子类先执行

  • 子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法.

  • 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)

下面我通过代码来打印验证:

//Person.m 
+(void)initialize {
     NSLog(@"%s",__FUNCTION__);
}

执行结果:

没有打印

- (void)viewDidLoad {
    [super viewDidLoad];
    
    return;
    //这块代码不会执行,也不会执行initialize
    [Person new];
}

执行结果:

没有打印

表明只要不执行类的第一方法,是不会执行的。

//执行这个代码
[Person new];

执行结果:

[Person initialize]

我们在Student.m(继承Person)中不实现initialize方法,

//Person.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Student.m
Student中不实现initialize方法

执行一下

[Student new];

直接结果:

[Person initialize]
[Person initialize]

为啥执行两遍,因为父类会比子类先执行,先执行父类的,子类没有实现的,会调用父类initialize方法,所以执行了两遍。
如果把上面的代码改成:

//在 Person.h
+(void)initialize
{
    if(self == [Person class])
    {
          NSLog(@"%s",__FUNCTION__);
    }
}

执行结果,只执行一遍。

那我们在Student也实现initialize方法:

//Person.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Student.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

执行一下

[Student new];

可以得出当子类实现initialize方法后,会覆盖父类initialize方法.

当Person的类别也实现initialize方法,如下:

//Person.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Person+Category.m
+(void)initialize {

      NSLog(@"%s",__FUNCTION__);
}

执行结果是:

[Person(Category) initialize]

运行后可以看到Person的initialize方法并没有被执行,已经被Person+Category中的initialize方法覆盖了。

如果是多个Person的Category都实现了initialize,执行结果会如何,如下:

//Person.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Person+Category.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Person+Category2.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

//Person+Category3.m
+(void)initialize {

     NSLog(@"%s",__FUNCTION__);
}

运行:

[Person new];

直接结果如下:

[Person(Category2) initialize]

initialize方法对一个类而言只会调用一次,类中initialize比Category的initialize优先级低,多个Category执行Compile Sources最后一个Category的initialize方法

Compile_Sources.png

看compile source 文件中顺序Person+Category2.m个category最后执行的。

Load使用情况

load很常见的一个使用场景,交换两个方法的实现,称之为method swizzling

+(void)load {
    
    static dispatch_once_t oneToken;

    dispatch_once(&oneToken, ^{

           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 ){
        // iphone处理
        UIImage * image =  [self swizze_imageNamed:name];
        if (image != nil) {
            return image;
        } else {
            return nil;
        }
    } 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;
        }
}

这个就是抵用系统imageNamed方法,在这基础上对ipad的图片进行适配,这样的每次调用imageNamed
不用在对ipad和iphone进行适配。

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];
    }
}

总结

  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方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。

结尾

load和initialize这块特别容易混淆了,所以大家多多实践,在实践中慢慢理解。大家加油!!!

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

推荐阅读更多精彩内容