9、iOS底层分析 - 类的加载

程序启动

  1. 静态库是直接编译进程序的
    1. 动态库是需要的时候动态添加的,dyld链接的就是动态库
    2. dyld 是苹果的动态链接器,每个程序依赖的动态库都需要通过dyld(位于/usr/lib/dyld)一个一个加载到内存。 加载的是镜像文件
    3. 如果每个程序运行的时候都重复的去加载,势必造成运行缓慢,为了优化启动速度和提高程序性能,就用到了共享缓存机制。所有默认的动态链接库被合并成一个大的缓存文件,放到/System/Library/Caches/com.apple.dyld/目录下,按不同框架分别保存
    4. 例如iPhone 里面就有dyld_shared_cache_armv7s和dyld_shared_cache_armv64两个文件,如下图所示。
    1. objc_init
      1. 提供运行时环境
      2. 程序启动后是如何从dyld到objc_init的?
        1. 递归初始化  system库(系统库)
        2. dispatch (调度)
        3. objc   ->  objc_init
        4. objc_init  中进行映射,将镜像文件中的数据映射出来映射到表里
  2. images 
    1. dyld 与 objc_init 通讯
    2. 这个镜像文件在dyld 中才有
    3. objc_init依赖的镜像文件
  3. 回调函数
    1. *snotif
    2. 注册
    3. 指针
  4. images 加载
    1. 在内存中用表存(内存中存的形式集合形式、table表形式,map)
    2. 镜像文件给读出来 – 存到内存中
      1. 读的数据都是什么?
      2. 二进制(类、协议、分类、sel)

 

类的加载

_objc_init

  1. 程序在启动时,先用dyld进行动态库的链接,做完一系列准备操作之后,会进入到_objc_init方法
  2. /***********************************************************************
    * _objc_init
    * Bootstrap initialization. Registers our image notifier with dyld.
    * Called by libSystem BEFORE library initialization time
    **********************************************************************/
    
    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // fixme defer initialization until an objc-using image is found?
        environ_init();   //读取环境变量
        tls_init();       //关于线程key的绑定
        static_init();    //系统级别的类,在这里进行初始化
        lock_init();      //是空实现!!这个方法里面什么都没写
        exception_init(); //初始化libobic的异常处理系统
    
        _dyld_objc_notify_register(&map_images, load_images, unmap_image); //注册处理程序,以便在映射,取消映射和初始化objc图像是调用。
    }
    

1、environ_init

  1. 读取影响运行时的环境变量,可以在终端输入 export OBJC_HELP=1 来打印环境变量帮助
  2. objc[3206]: Objective-C runtime debugging. Set variable=YES to enable.
    objc[3206]: OBJC_HELP: describe available environment variables
    objc[3206]: OBJC_PRINT_OPTIONS: list which options are set
    objc[3206]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
    objc[3206]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
    objc[3206]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
    objc[3206]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
    objc[3206]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
    objc[3206]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
    objc[3206]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
    objc[3206]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
    objc[3206]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
    objc[3206]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
    objc[3206]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
    
    

2、tls_init()

  1. 关于线程key的绑定 – 比如每个线程数量的析构函数
  2. 析构函数:析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。

3、static_init()

  1. 运行C++静态构造函数。在dyld调用我们的静态构造函数之前,“libc”会调用“_objc_init()”,因此我们必须自己做。
  2. 这个地方一定要比dyld(_dyld_objc_notify_register)靠前,因为这个是系统级别的,所必须要比dyld靠前进行初始化
  3. 这是系统级别的类,在这里进行初始化。libc在dyld调用静态构造函数之前调用_objc_init(),
  4. /***********************************************************************
    * static_init
    * Run C++ static constructor functions.
    * libc calls _objc_init() before dyld would call our static constructors, 
    libc在dyld调用静态构造函数之前调用_objc_init(),
    * so we have to do it ourselves.
    **********************************************************************/
    static void static_init()
    {
        size_t count;
        auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
        for (size_t i = 0; i < count; i++) {
            inits[i]();
        }
    }

4、lock_init()

  1. 就是说objc的异常是完券才用C++那一套的 – 本身就是通过C++写的
  2. 是空实现!!这个方法里面什么都没写

5、_dyld_objc_notify_register

前面的都是准备条件,_dyld_objc_notify_register,通过注释可以知道该方法仅仅只用来在objc的运行时使用 ,进行map_images load_images

_dyld_objc_notify_register(&map_images, load_images, unmap_images)

  1. 仅供objc运行时使用,objc在这里是运行时环境
  2. 注册处理程序,以便在映射,取消映射和初始化objc图像是调用。
  3. dyld将使用包含objc_image_info的镜像文件的数组回调“mapped”函数
  4. map_images
    1. 读取镜像文件
  5. load_images
  6. unmap_image
    1. 程序终止、异常等,内存里干掉部分镜像文件

未完 。。。 待续

发布了83 篇原创文章 · 获赞 12 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/shengdaVolleyball/article/details/104068747