美文网首页基础
问题:+(void)load; +(void)initializ

问题:+(void)load; +(void)initializ

作者: 姜小舟 | 来源:发表于2020-08-21 09:50 被阅读0次

    两个方法都可以进行一些类的初始化操作。其中有些小区别。

    +(void)load 方法

    • +load 方法调用时机:+load 方法是当类被添加 Objective-C Runtime 时调用的,类调用的顺序:父类优先于子类,子类优先于分类(有多个分类,调用顺序是看分类文件的编译顺序)

    • +load 方法被调用的方式:通过+load 方法最核心的源码可以看到,调用方式是 (*load_method)(cls, SEL_load); ,这里是直接使用函数内存地址方式调用,而不是用 objc_masSend 发消息的机制;
      直接利用函数地址调用的方法,没有走消息转发机制,就会出现这样子的现象:父类、子类、分类的 +load 互不影响,假设子类没有实现 +load 方法,也不会去调用多一次父类的 +load 方法。

    • +load 方法可以做什么:由于 +load 方法是在 Runtime 加载类时调用的,执行时机是比较早的,在这里我们可以做 Method Swizzling,对一些函数进行 Hook。(无痕埋点、AOP 都可以利用这个机制来实现)。

      +load 方法最核心的源码

      static void call_class_loads(void) {
         int I;
      
         // Detach current loadable list.
         struct loadable_class *classes = loadable_classes;
         int used = loadable_classes_used;
         loadable_classes = nil;
         loadable_classes_allocated = 0;
         loadable_classes_used = 0;
      
          // Call all +loads for the detached list.
          for (i = 0; i < used; i++) {
              Class cls = classes[i].cls;
              load_method_t load_method = (load_method_t)classes[i].method;
              if (!cls) continue;
      
              if (PrintLoading) {
                  _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
              }
            //核心调用方式
              (*load_method)(cls, SEL_load);
          }
      
          // Destroy the detached list.
          if (classes) _free_internal(classes);
      }
      

    +(void)initialize 方法

    • +initialize 方法调用时机:+initialize 方法是在类或它的子类收到第一条消息之前被调用的,看下面runtime 源码
      当给一个类发送消息的时候,runtime 会调用 lookUpImpOrForward 函数,这里会对没有初始过的类进行初始化,调用 _class_initialize 函数。

    • +initialize 方法被调用的方式:看下面+initialize 方法最核心的源码,通过这个函数,我们可以知道两点:
      1.递归调用 +initialize 方法的,父类比子类先初始化;
      2.利用 objc_msgSend 发消息模式调用 +initialize 方法的(这里与其它普通方法调用一致)

      故我们可以知道,如果子类没有实现 +initialize 方法,那么继承自父类的实现会被调用;如果一个类的分类实现了 +initialize 方法,那么就会对这个类中的实现造成覆盖。

    • +initialize 方法可以做什么:+initialize 方法调用的时机也是非常早的,并且是懒加载模式,可以在 +initialize 方法里面做一些初始化的工作。

    runtime 源码如下:

    IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
                         bool initialize, bool cache, bool resolver) {
      ...
          rwlock_unlock_write(&runtimeLock);
      }
    
      if (initialize  &&  !cls->isInitialized()) {
          _class_initialize (_class_getNonMetaClass(cls, inst));
          // If sel == initialize, _class_initialize will send +initialize and 
          // then the messenger will send +initialize again after this 
          // procedure finishes. Of course, if this is not being called 
          // from the messenger then it won't happen. 2778172
      }
    
      // The lock is held to make method-lookup + cache-fill atomic 
      // with respect to method addition. Otherwise, a category could 
      ...
    }
    

    +initialize 方法最核心的源码

    void _class_initialize(Class cls) {
      ...
      Class supercls;
      BOOL reallyInitialize = NO;
    
      // Make sure super is done initializing BEFORE beginning to initialize cls.
      // See note about deadlock above.
      supercls = cls->superclass;
      if (supercls  &&  !supercls->isInitialized()) {
          _class_initialize(supercls);
      }
    
      // Try to atomically set CLS_INITIALIZING.
      monitor_enter(&classInitLock);
      if (!cls->isInitialized() && !cls->isInitializing()) {
          cls->setInitializing();
          reallyInitialize = YES;
      }
      monitor_exit(&classInitLock);
    
      if (reallyInitialize) {
          // We successfully set the CLS_INITIALIZING bit. Initialize the class.
    
          // Record that we're initializing this class so we can message it.
          _setThisThreadIsInitializingClass(cls);
    
          // Send the +initialize message.
          // Note that +initialize is sent to the superclass (again) if 
          // this class doesn't implement +initialize. 2157218
          if (PrintInitializing) {
              _objc_inform("INITIALIZE: calling +[%s initialize]",
                           cls->nameForLogging());
          }
    
          ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    
          if (PrintInitializing) {
              _objc_inform("INITIALIZE: finished +[%s initialize]",
      ...
    }
    

    +load 方法 和 +initialize 方法比较

    +load +initialize
    调用时机 被添加到 runtime 时 收到第一条消息前,可能永远不调用
    调用顺序 父类->子类->分类 父类->子类
    调用次数 1次 多次
    是否需要显式调用父类实现
    是否沿用父类的实现
    分类中的实现 类和分类都执行 覆盖类中的方法,只执行分类的实现

    相关文章

      网友评论

        本文标题:问题:+(void)load; +(void)initializ

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