zoukankan      html  css  js  c++  java
  • iOS Category实现原理 (补充)

    iOS Category实现原理 (补充)

    load 和 initialize

    load

    1. load方法会在程序启动就会调用,当装载类信息的时候就会调用。

      • 调用顺序看一下源代码。在 objc-loadmethod.m 文件中实现
      void call_load_methods(void)
      {
          static bool loading = NO;
          bool more_categories;
      
          loadMethodLock.assertLocked();
      
          // Re-entrant calls do nothing; the outermost call will finish the job.
          if (loading) return;
          loading = YES;
      
          void *pool = objc_autoreleasePoolPush();
      
          do {
              // 1. Repeatedly call class +loads until there aren't any more
              // 1.调用类的 load 方法
              while (loadable_classes_used > 0) {
                  call_class_loads();
              }
      
              // 2. Call category +loads ONCE
      		// 2.调用分类的 load 方法
              more_categories = call_category_loads();
      
              // 3. Run more +loads if there are classes OR more untried categories
          } while (loadable_classes_used > 0  ||  more_categories);
      
          objc_autoreleasePoolPop(pool);
      
          loading = NO;
      }
      
    2. 通过源码我们发现是优先调用类的load方法,之后调用分类的load方法。

    3. 查看load方法的调用源码,在 objc-loadmethod.m 文件中

    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]
    ", cls->nameForLogging());
            }
            (*load_method)(cls, SEL_load);
        }
        
        // Destroy the detached list.
        if (classes) free(classes);
    }
    
    1. 我们看到load方法中直接拿到load方法的内存地址直接调用方法,不在是通过消息发送机制调用。
    2. 所以原始类的load方法并不会被覆盖,且调用类的load方法之前会保证其父类已经调用过load方法。

    initialize

    1. 当类第一次接收到消息时,就会调用initialize,相当于第一次使用类的时候就会调用initialize方法。调用子类的initialize之前,会先保证调用父类的initialize方法。如果之前已经调用过initialize,就不会再调用initialize方法了。当分类重写initialize方法时会先调用分类的方法。

      • 看一下initialize的源码
      void callInitialize(Class cls)
      {
          ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
          asm("");
      }
      
    2. 由此我们发现,initialize是通过消息发送机制调用的,消息发送机制通过isa指针找到对应的方法与实现,因此先找到分类方法中的实现,会优先调用分类方法中的实现。

    总结

    1. Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
      • Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法。
    2. load、initialize的区别,以及它们在category重写的时候的调用的次序。
    • 调用方式:
      • load是根据函数地址直接调用,initialize是通过objc_msgSend调用
    • 调用时刻:
      • load是runtime加载类、分类的时候调用(只会调用1次),
      • initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)
    • 调用顺序:
      • 先调用类的load方法,先编译那个类,就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法,先编译的分类优先调用load方法。
      • initialize先初始化父类,之后再初始化子类。如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),如果分类实现了+initialize,就覆盖类本身的+initialize调用。
  • 相关阅读:
    C#面向对象的三大特性概述
    SQL Server 低版本还原高版本的数据库
    将表转化成脚本的存储过程
    iis安装失败解决方法
    kafka consumer 配置详解
    C#解析XML文件
    blat
    REST接口POST方法发送文件到服务器(C#)
    http://www.codeproject.com/Questions/117324/uploadfileincwithHttpWebRequest
    PDF Password Remover
  • 原文地址:https://www.cnblogs.com/ShaoYinling/p/10239861.html
Copyright © 2011-2022 走看看