美文网首页
备忘录之-+load和+initialize

备忘录之-+load和+initialize

作者: 忧郁的小码仔 | 来源:发表于2019-12-22 14:35 被阅读0次

    +load和+initialize方法都是老生常谈了,最近有段时间没写iOS了,再把一些基础的东西整理下,方便自己后边能随时捡回来参考一下。

    说起这两个方法,还要从一个app的加载启动开始,我们都知道我们自己的代码都是从main方法开始的,其实在main之前,从我们点击app图标开始,系统就帮我做了一些列的初始化和准备工作,具体的内容请参考iOS程序main函数之前发生了什么

    就像大神文中提到的那样,dyld(动态连接器)先将程序二进制文件初始化,接着ImageLoader会读取image(包含我们的类、方法等符号),当image加载到内存后,dyld会通知runtime进行处理,runtime接手后就会遍历所有加载进来的class,并按继承顺序调用class的 +load方法和其Category的+load方法。

    +load

    上面说过,当class被加载进进程地址空间后,如果class本身自己实现了+load方法,runtime就会给它发送load消息。在这之前runtime会先发送load消息给它的父类以及所有连接的公共库。需要注意的是load消息是不会走继承的,如果子类没有写+load方法,即使父类写了也不会调用父类的+load方法。

    另外,我们自己的其它类有没有接到load消息,我们是没法确定的,因为类加载的顺序是不确定的。同时,当Category被加载的时候,runtime也会给它发送load消息,如果多个Category同时定义了同一个类的相同方法,只会有一个方法会被注册并使用,其它的同名方法永远不会被调用到了。

    +load方法用的最多的地方就是AOP切面编程,比如数据打点。🌰就不写了,今天翻了下很久之前的博客,发现还有一个很有意思的用法,用+load来给AppDelegate瘦身:
    我们一般都会在application:didFinishLaunchingWithOptions中做一些第三方库的初始化和注册等工作,比如友盟,极光等等,这些东西多了之后虽然没有大的影响,但是让谁看起来都不是特别舒服,我之前都是丢到单独的方法里,其实,原来还可以这么做:

    +(void)load {
        __block  id  observer = [[NSNotificationCenter  defaultCenter]  addObserverForName: UIApplicationDidFinishLaunchingNotification  object:nil  queue:nil  usingBlock:^(NSNotification *note){
        // 做第三方库的初始化操作
        [[NSNotificationCenter  defaultCenter]  removeObserver:observer];
    }]
    }
    

    这样,AppDelegate中的代码就能减少一些了。这里因为+load方法一定会在application:didFinishLaunchingWithOptions回调之前被调用,保证能够接收到通知。另外这里block对observer对象的捕获会早于函数的返回,所以要加__block,不然会捕获到nil。

    +initialize

    runtime会在给类对象或者类的实例发送第一条消息之前调用initialize方法。和+load不同的是,调用initialize走的是正常的消息发送,会走继承,所以,如果子类没有实现initialize,而父类实现了的话,子类就会使用父类的initialize方法。所以父类的initialize可能会被多次执行。需要注意的是,如果所有的子类都实现了自己的initialize方法,那么父类的initialize只会调用一次,如果有的子类没有实现自己的initialize,那么父类的initialize就会被调用(这也是父类会被调用多次的原因)

    为了避免多次执行,苹果推荐的实现initialize的正确姿势是:

    +(void)initialize {
        if (self == [SomeClass class]) {
            // 这里处理要做的事情,比如静态变量的初始化等等
        }
    }
    

    综上,+load方法只要class被加载进进程地址空间了,就会被调用,不管这个类有没有用,而+initialize只有在接收到消息的时候才会被调用,在它之前,+load已经执行了。

    几个小的注意事项:
    1.这两个方法都不需要调用super,runtime会按照继承顺序递归调用。
    2.由于动态链接过程中,所有的依赖库的类会先于我们自己的类加载,所以,我们可以在自己class的+load方法中替换系统库中的某个类的实现
    3.我们知道main方法内部套了一个autoreleasepool,那么+load需要吗?答案是不需要,runtime已经帮我们在load前后添加了 objc_autoreleasePoolPush()和objc_autoreleasePoolPop()方法,正如runloop中对一些资源的处理一样。

    参考连接:
    iOS 程序 main 函数之前发生了什么
    Notification Once
    NSObject +load and +initialize - What do they do?

    相关文章

      网友评论

          本文标题:备忘录之-+load和+initialize

          本文链接:https://www.haomeiwen.com/subject/dpdonctx.html