iOS开发iOS学习笔记iOS学习开发

OC 中的 load 与 initialize 方法

2017-06-04  本文已影响172人  CoderHG

这里所要介绍的 loadinitialize 方法,这两个是类方法,是系统的方法。我曾经见过有大神在自己的class中写了一个 initialize对象 方法,无知的我在那里绕了半天。

建议大家,从这里下载我的样例项目。

一定要记得下载loadAndinitialize

一、先抛出问题

二、load

在文件刚开始装载的时候被调用(在main方法被调用之前),并有且仅调用一次。

运行项目Load,打印结果如下:

2017-06-04 23:20:11.855869+0800 Load[7892:2777060] OtherObject
2017-06-04 23:20:11.856272+0800 Load[7892:2777060] ParentObject
2017-06-04 23:20:11.856298+0800 Load[7892:2777060] ChildObject
2017-06-04 23:20:11.856321+0800 Load[7892:2777060] ChildObject+Category

load 的执行顺序
各位大神的建议

load类方法 中,尽量简洁,不要写复杂的逻辑。一般可以将 Swizzled 方法在 load类方法 中实现。

三、initialize

在当前class第一次被用到的时候被调用(第一次发送消息),同样有且仅调用一次。所有,也有很多大神说这个方法有懒加载的感觉,用到才会触发。不像 load类方法 那样,一旦装载就赶紧触发。

情景一

打开 项目initialize 直接运行,会发现没有打印日志。说明在没有给当前class发送消息,是不会触发 initialize类方法 的。

情景二

ViewController.m 文件中仅打开 test1 方法,运行项目,打印日志如下:

2017-06-05 00:15:06.312925+0800 Initialize[7959:2792250] ParentObject = ParentObject
2017-06-05 00:15:06.313506+0800 Initialize[7959:2792250] ChildObject = ChildObject

情景三

ViewController.m 文件中仅打开 test1 方法的前提下,将 ChildObject 中的 initialize类方法 注释掉,运行项目,打印日志如下:

2017-06-05 00:16:17.099422+0800 Initialize[7963:2793122] ParentObject = ParentObject
2017-06-05 00:16:17.099534+0800 Initialize[7963:2793122] ParentObject = ChildObject

情景二 与 情景三 同时说明: 父类中的 initialize类方法 会被子类触发。在子类中即使没有实现 initialize类方法 ,也会默认的去调用父类的 initialize类方法 。所以在实现 +initialize 方法的时候一定要做好判断,放在当前 Class 被继承导致一些重复的操作(在代码中已经做了判断)。

在进行之后的情景之前,记得将 ChildObject 中的 initialize类方法 打开。

情景四

ViewController.m 文件中仅打开 test2 方法,运行项目,打印日志如下:

2017-06-05 00:27:31.883299+0800 Initialize[7968:2795570] ParentObject = ParentObject
2017-06-05 00:27:31.883410+0800 Initialize[7968:2795570] ChildObject = ChildObject
2017-06-05 00:27:31.883463+0800 Initialize[7968:2795570] ------华丽的分割线------

情景五

ViewController.m 文件中仅打开 test3 方法,运行项目,打印日志如下:

2017-06-05 00:29:47.599994+0800 Initialize[7971:2796349] ParentObject = ParentObject
2017-06-05 00:29:47.600114+0800 Initialize[7971:2796349] ------华丽的分割线------
2017-06-05 00:29:47.600160+0800 Initialize[7971:2796349] ChildObject = ChildObject

情景四 与 情景五 同时说明: initialize类方法 有且仅调用一次。

细心的大神,应该发现了点漏洞了。会问 情景三 又是怎么回事? 在 ParentObject 类中明明被触发了两次。再仔细一看,其实有一个是子类触发的,看 self 的打印值是 ChildObject 就明白了。那一般应该怎么处理这种情况呢?解决方案是,在 ParentObject 类中的 initialize类方法 中作这样的判断即可:

+ (void)initialize {
    NSLog(@"ParentObject = %@", self);
    
    if (self == [ParentObject class]) {
        // TODO: 做自己喜欢做的事
    }
}

initialize类方法 中,一般都是作一些全局性的一次性设置。比如 UITabBarControllerUINavigationController 全局的统一设置。

比如,我常用的:

+ (void)initialize
{
    UINavigationBar *navigationBar = [UINavigationBar appearance];
    
    [navigationBar setBackgroundImage:[UIImage imageWithColor:GlobalColor] forBarMetrics:UIBarMetricsDefault];
    if ([UINavigationBar instancesRespondToSelector:@selector(setShadowImage:)]) {
        [navigationBar setShadowImage:[UIImage imageWithColor:[UIColor clearColor]]];
    }
    // 统一修改控制器title的字体颜色
    NSMutableDictionary* dictM = [NSMutableDictionary dictionary];
    dictM[NSFontAttributeName] = Font(19);
    dictM[NSForegroundColorAttributeName] = [UIColor whiteColor];
    [navigationBar setTitleTextAttributes:dictM];
}

四、load 与 initialize 同时出现

打开 项目 02LoadandInitialize, 直接运行,打印日志如下:

2017-06-05 00:55:47.278158+0800 02LoadandInitialize[7976:2801773] initialize = LoadandInitializeObject
2017-06-05 00:55:47.278589+0800 02LoadandInitialize[7976:2801773] load = LoadandInitializeObject

两个问题来了:

其实,在 项目 02LoadandInitialize 中就能找到答案。如果找不到原因,请直接打开 项目 03LoadandInitialize 运行一下,打印日志如下:

2017-06-05 01:04:30.237773+0800 03LoadandInitialize[7979:2804252] load 开始执行
2017-06-05 01:04:30.238219+0800 03LoadandInitialize[7979:2804252] initialize = LoadandInitializeObject
2017-06-05 01:04:30.238271+0800 03LoadandInitialize[7979:2804252] load = LoadandInitializeObject
2017-06-05 01:04:30.238348+0800 03LoadandInitialize[7979:2804252] load 执行即将结束

终于真相大白了,原来是在 load 方法中调用了[self class] 导致的。所以一般是强烈不推荐在 +load 方法中调用当前 Class 与其他 Class 的任何方法的。如果在当前的 Class 的 +load 中调用其它 Class 的方法,一旦出现问题,是很难排查的。

六、一个小总结

七、分类中重写 +load 与 +initialize

具体的原因是两个方法的调用逻辑不一样。
+load 方法是在 dyld 加载的过程中被调用,调用的逻辑是一旦检测到+load 方法被重写,那么直接就调用。
+initialize 方法的调用逻辑与通常的消息发送机制一致,都是通过当前 Class 的 isa 来进行查找方法调用。其中 Class 的 isa 的值就是其对应的元类(meta-class)。

为什么分类中重写了原生 Class 中的方法之后,原生 Class 中的方法失效?
对于一个 Class 来说,所有的分类方法都被放于原生 Class 方法的前面。可以看看下面这张图。

image.png

所以一旦分类中重写了原生类中的方法,那么原生类中的方法就永远没有机会被调用。

谢谢~

上一篇下一篇

猜你喜欢

热点阅读