返回

深入浅出:iOS 底层原理之 _objc_init() & _read_images()

IOS

IOS 底层原理之 _objc_init() & _read_images()

前言

在深入探索 iOS 底层原理时,_objc_init()_read_images() 两个函数扮演着至关重要的角色。_objc_init() 将 Objective-C Runtime 注册到 dyld 进程加载器中,而 _read_images() 负责加载 Objective-C 镜像文件,包含方法实现、类定义和其他相关数据。本文将深入探讨这两个函数在 iOS 应用程序启动过程中所扮演的角色。

_objc_init()

_objc_init() 函数在 Objective-C Runtime 初始化过程中被调用,它是 dyld 进程加载器回调函数之一。其主要职责是将 Objective-C Runtime 注册到 dyld 中,允许 dyld 在加载 Objective-C 镜像文件时调用 Runtime 函数。_objc_init() 函数还负责初始化 Objective-C 数据结构,例如类元数据和方法实现列表。

步骤:

  1. 注册回调函数: _objc_init() 向 dyld 注册三个回调函数,这些函数将在加载 Objective-C 镜像文件时被调用:

    • _objc_image_init(): 处理镜像文件初始化
    • _objc_load_image_info(): 加载镜像文件中的方法实现和类信息
    • _objc_eaten_data_destructor(): 在镜像文件不再需要时释放其数据
  2. 初始化 Objective-C 数据结构: _objc_init() 初始化以下 Objective-C 数据结构:

    • 根元类: NSObject 的元类
    • 元类注册表: 用于存储元类的哈希表
    • 方法列表: 用于存储方法实现的哈希表

_read_images()

_read_images() 函数负责加载 Objective-C 镜像文件。它遍历 dyld 加载的镜像文件列表,并为每个 Objective-C 镜像文件调用 _objc_image_init() 回调函数。_objc_image_init() 回调函数随后调用 _objc_load_image_info() 加载镜像文件中的方法实现和类信息。

步骤:

  1. 获取镜像文件列表: _read_images() 从 dyld 获取已加载镜像文件列表。

  2. 遍历镜像文件列表: _read_images() 遍历每个镜像文件:

    • 如果镜像文件是 Objective-C 镜像文件(即,包含 __OBJC 段),则调用 _objc_image_init()
    • 如果镜像文件不是 Objective-C 镜像文件,则跳过它。
  3. _objc_image_init(): _objc_image_init() 根据镜像文件内容初始化镜像数据结构:

    • 加载方法实现: 从镜像文件中读取方法实现并将其添加到方法列表中。
    • 加载类信息: 从镜像文件中读取类信息并将其添加到元类注册表中。

总结

_objc_init()_read_images() 函数在 iOS 应用程序启动过程中扮演着至关重要的角色。_objc_init() 将 Objective-C Runtime 注册到 dyld 中,而 _read_images() 负责加载 Objective-C 镜像文件。这些函数共同确保 Objective-C 代码在 iOS 应用程序中得到正确执行。