iOS+load和+initialize详解

转自:
iOS类方法load和initialize详解
iOS - + initialize 与 +load

load函数调用特点如下:

+(void)load
什么时候调用,当程序一启动的时候就会调用
作用:将当前类加载进内存,放在代码区

+load 方法会系统被默认执行,并且是在main函数之前执行的,与这个类是否被用到无关,

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

下面通过实例来一起验证下:
我们新建2个类:Person继承NSObject,Student和Teacher均继承Person

Person : NSObject
Student : Person
Teacher : Person

新建3个Person分类:

Person (Category)
Person (Category2)
Person (Category3)

在Person,Student,Person (Category),Person (Category2),Person (Category3)的.m文件中实现下面方法(Teacher除外)

//In Person.m
+(void)load
{
    NSLog(@"%s",__FUNCTION__);
}
//In Student.m(继承自Person)
+(void)load
{
    NSLog(@"%s",__FUNCTION__);
}

//In Teacher.m(继承Person)
不实现load方法

//In Person+Category.m
+(void)load
{
    NSLog(@"%s",__FUNCTION__);
}
//In Person+Category2.m
+(void)load
{
    NSLog(@"%s",__FUNCTION__);
}
//In Person+Category3.m
+(void)load
{
    NSLog(@"%s",__FUNCTION__);
}
运行结果:
+[Person load]
+[Student load]
+[NSObject(Category3) load]
+[NSObject(Category1) load]
+[NSObject(Category2) load]

可以看到执行顺序依次为:

1.首先执行的是父类Person load方法,再执行的是子类Student load方法,说明父类的load方法执行顺序要优先于子类
2.子类Teacher中没有实现load方法,没有打印,说明子类没有实现load方法时并不会调用父类load方法
3.最后执行的是Person 3个Category的load方法,并且没有顺序,说明类别(Category)中的load方法要晚于类,
多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定
4.同时我们也可以看到这几个Category load 执行顺序与其在Compile Sources中出现的顺序一致
5.当然多个不同的类 其load执行顺序,也与其在Compile Sources出现的顺序一致

initialize函数调用特点如下:

initialize在类或者其子类的第一次初始化这个类之前调用。即使类文件被引用进项目,但是没有使用,initialize不会被调用。

  • initialize 方法类似一个懒加载,如果没有使用这个类,那么系统默认不会去调用这个方法,且默认只加载一次;
  • initialize 的调用发生在 +init 方法之前。
1.父类的initialize方法会比子类先执行
2.当子类未实现initialize方法时,会调用父类initialize方法,
子类实现initialize方法时,会覆盖父类initialize方法.
3.当有多个Category都实现了initialize方法,会覆盖类中的方法,
只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)

我们同样通过实例来一起验证下:

说明: 只是把类文件被引用进项目,没有使用的话,initialize不会被调用

我们添加Person类,在.m中实现+ initialize方法

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

运行结果:
无打印...
啥也没打印
说明:只是把类文件被引用进项目,没有使用的话,initialize不会被调用
我们在Teacher(继承Person).m中不实现initialize方法

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

//In Teacher.m(继承自Person)

Teacher.m中不实现initialize方法

我们调用下Teacher的new方法,运行

[Teacher new];

运行结果:
+[Person initialize]
+[Person initialize]
运行后发现父类的initialize方法竟然调用了两次:
可见当子类未实现initialize方法,会调用父类initialize方法.
但为什么会打印2次呢?
我的理解:
子类不实现initialize方法,会把继承父类的initialize方法并调用一遍。
在此之前,父类初始化时,会先调用一遍自己initialize方法.所以出现2遍,
所以为了防止父类initialize中代码多次执行,我们应该这样写:
//In Person.m
+(void)initialize
{
    if(self == [Person class])
    {
          NSLog(@"%s",__FUNCTION__);
    }
}

下面我们在 Teacher.m中实现initialize方法
//In Person.m
+(void)initialize
{
     NSLog(@"%s",__FUNCTION__);
}

//In Teacher.m(继承自Person)
+(void)initialize
{
     NSLog(@"%s",__FUNCTION__);
}

同样调用Teacher 的new方法,运行

[Teacher new];

运行结果:
+[Person initialize]
+[Teacher initialize]
可以看到 当子类实现initialize方法后,会覆盖父类initialize方法,这一点和继承思想一样

我们在Person.m和Person+Category.m中实现initialize方法
//In Person.m
+(void)initialize
{
     NSLog(@"%s",__FUNCTION__);
}

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

调用Person 的 new方法,运行
[Person new];

运行结果:
2017-05-05 09:46:51.054 LoadAndInitializeExample[38773:1226306] +[Person(Category) initialize]
运行后可以看到Person的initialize方法并没有被执行,已经被Person+Category中的initialize取代了
当有多个Category时会怎样了,我们在Person,Person+Category,Person+Category2,Person+Category3 的.m中都实现initialize方法
//In Person.m
+(void)initialize
{
     NSLog(@"%s",__FUNCTION__);
}

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

调用Person new方法,运行
[Person new];
运行结果:
2017-05-05 09:49:38.853 LoadAndInitializeExample[38825:1227819] +[Person(Category2) initialize]
可以看到,当存在多个Category时,也只执行一个,具体执行哪一个Category中的initialize方法,测试后便可发现,会执行Compile Sources 列表中最后一个Category 的initialize方法

什么情况下使用:

由于调用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方法主要用来对一些不方便在编译期初始化的对象进行赋值。比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:

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

注意: 在initialize方法收到调用时,运行环境基本健全。 initialize内部也使用了锁,所以是线程安全的。但同时要避免阻塞线程,不要再使用锁

总结:
load和initialize的共同点
1.如果父类和子类都被调用,父类的调用一定在子类之前
2.自动调用父类,不需要super操作; load应该是在load方法内部实现, 系统会帮我们调用super load。不用我们在显示调用 , 自动调用仅仅会调用一次(不包括外部显示调用)。

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

推荐阅读更多精彩内容

  • iOS开发中总能看到+load和+initialize的身影,网上对于这两个方法有很多解释,官方也有说明,但有些细...
    朱晓辉阅读 27,335评论 19 139
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,012评论 8 265
  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,192评论 0 11
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,034评论 1 32
  • 按照某位抠脚大汉的话来说,那是拥有着明媚的阳光,注定着要有故事的一天... 你端坐在课桌边,时而抖腿,时而脱鞋,...
    忱天下阅读 180评论 0 0