iOS 小知识-load()&initialize()

前言

之前在面试中被问到关于 loadinitialize,虽然之前在网上有看到过相关的资料,但是却没有仔细看过,也没有实际得在项目中使用过,所以被问到就一脸闷逼。。最近在看一些基础的知识,对于这两个方法也有点了解,所以做个整理。

介绍

这两个方法都是NSObject下用来初始化类的类方法。

//Swift
class func initialize()
//Objective-C
+ (void)load;
在类接受第一个消息前初始化这个类        
//Swift
class func load()
//Objective-C
+ (void)initialize;
每当类或者分类被添加到Objective-C运行时的时候调用;实现此方法在加载的时候执行类的特定的行为。    

load()

对于加入运行时的每个类以及分类来说,一定会调用这个方法,而且仅仅调用一次。
初始化的顺序如下:
注意:

  • 一个类的 +load 方法会在它所有的父类的 +load 方法后调用。
  • 一个分类的 +load 方法会在类本身的 +load 方法之后调用。
  • +load 方法不遵循继承规则, 如果某个类本身没有实现 +load 方法,不管其父类有无实现 +load 方法,系统都不会调用。
  • +load 方法务必实现得精简些,尽量减少其所需要执行的操作,整个程序可能因为 +load 方法而堵塞。
  • +load 方法真正的用途是在调试程序。

问题:
load 方法的问题在于,在执行子类 load 方法之前, 必定会先执行所有父类的 load 方法, 如果还依赖了其他程序库,那么其他程序库里相关类对的 load 方法也必定会先执行。然而,根据某个给定的程序库,却无法判断出其中个各类的载入顺序。所以,在 load 方法中使用其他类是不安全的。

重要
对于自定义的 load 方法的实现的 Swift 类桥接到 Objective-C 是不会自动调用的。

扩展

我们知道OC 中有一个东西叫: Method Swizzling。我们可以通过它来做埋点的工作,当时这只是它的用法之一,它还有很多用法,这里就不一一说明了。使用Method Swizzling 来做埋点(用户行为统计 统计用户点击事件以及页面跳转等操作,上传到服务器做数据分析等操作)可以使得埋点代码与业务代码分离,可复用性好。这里需要用到 Runtime 方面的知识,这里就不细讲。在使用 Method Swizzling 的时候方法交换的操作是在 load 方法中进行的,在分类中重写 load 方法,而且Swizzling should always be done in +load.

There are two methods that are automatically invoked by the Objective-C runtime for each class. +load is sent when the class is initially loaded, while +initialize is called just before the application calls its first method on that class or an instance of that class. Both are optional, and are executed only if the method is implemented.

Because method swizzling affects global state, it is important to minimize the possibility of race conditions. +load is guaranteed to be loaded during class initialization, which provides a modicum of consistency for changing system-wide behavior. By contrast, +initialize provides no such guarantee of when it will be executed—in fact, it may never be called, if that class is never messaged directly by the app.

load 方法总是会在类初始化的时候被调用,这给改变系统范围的操作提供了保障,然而 initialize 方法却不能保证一定会被调用,它可能永远不会被调用如果类一个不被使用(懒加载)。所以 Swizzling总是应该写在 load 方法里。

代码如下:

#import <objc/runtime.h>

@implementation UIViewController (Tracking)
//重写load 方法,Swizzling应该总是写在load方法里
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        //获取两个selector
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);
        
        //通过selector获取方法
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        //这里的操作是判断类中是否已经有需要替换的方法的实现。
        BOOL didAddMethod =
            class_addMethod(class,
                originalSelector,
                method_getImplementation(swizzledMethod),
                method_getTypeEncoding(swizzledMethod));
        //如果类中已经有了需要替换的方法的实现,那么就将需要将替换原方法实现的实现替换为原方法的实现(解释起来听绕口的。。233333),没有就直接交换两个方法的实现。
        if (didAddMethod) {
            class_replaceMethod(class,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling
//这里的方法来替换原来viewWillAppear,在这个方法里写上埋点的代码
- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

@end

参考资料:

initialize()

运行时系统会在这个类或者是继承于这个类的子类接受第一条消息之前向这个类发送 initialize() 方法。父类在子类之前接受这个消息。

运行时系统通过一种线程安全的方式向类发送initialize消息。也就是说,initialize方法是在发送给类的第一个消息的第一个线程中执行的,其他的线程只有等到initialize 方法执行完成后,才能继续向这个类发送消息。

父类实现的initialize 方法可以会被多次调用如果子类没有实现 initialize 方法——运行时系统会调用继承的方法实现或者是子类明确调用了 [super initialize]. 如果你想保护你自己避免调用多次,你可以按照下面的方法来实现你的initialize 方法:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

因为initialize()是以堵塞的方式调用,所以限制类所需要做的事情就很有必要。尤其是任何需要用到锁的代码在其他类的initialize 中调用可能造成死锁。因此,你不能依靠 initialize 方法来做复杂的初始化,应该在使其变得简单,初始化类的本地变量。

特别需要注意的点

initialize() 每个类只会调用一次。如果你给类或者分类做一些独立的初始化,那么你应该去实现 load() 方法。

总结

  • 在加载阶段,如果类实现了 load() 方法, 那么系统就会调用它。分类也可以定义此方法,类中的 load() 方法比分类中的先调用。与其他方法不同,load() 方法不参与重写机制。
  • 首次使用某个类之前, 系统会向其发送 initialize 消息。此方法遵循普通的重写规则,所以需要在里面判断当前是哪个类需要初始化。
  • load()initialize() 方法都需要实现的精简一些,对于程序的相应能力有好处,也避免造成死锁等不必要的麻烦。
  • initialize() 方法中可以初始化类的一些本地变量。

本文是对于Effective Objective-C 2.0 这本书的部分总结,参考了苹果官方文档以及一些大神的博客。如果不对的地方请指出。蟹蟹。欢迎交流。

参考资料:

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

推荐阅读更多精彩内容