返回

剖析iOS类加载流程(下)

IOS

前言

在上一篇文章《iOS类加载流程分析(上)》中,我们已经探讨了ObjC源码中read_images函数一半的代码流程,所有本篇文章将对其下半部分的代码流程进行探究与分析。

学习重点

  1. _read函数
  2. imp_lookup函数
  3. load_images函数
  4. _objc_enter_runtime函数

_read函数

_read函数是ObjC类加载的核心函数,它负责从Mach-O文件中读取Objective-C类和协议的信息。_read函数的调用过程如下:

- (BOOL)readImages:(const char **)images count:(unsigned int)count
{
    BOOL success = YES;
    for (unsigned int i = 0; i < count; i++) {
        success &= _read(images[i]);
    }
    return success;
}

从代码中可以看到,_read函数首先遍历所有需要加载的Mach-O文件,然后依次调用_read函数来读取每个Mach-O文件中的Objective-C类和协议的信息。

_read函数的实现如下:

- (BOOL)_read:(const char *)imagePath
{
    // ...
    // 省略一些代码
    // ...

    struct mach_header_64 header;
    int fd = open(imagePath, O_RDONLY);
    if (fd == -1) {
        return NO;
    }
    if (read(fd, &header, sizeof(header)) != sizeof(header)) {
        return NO;
    }
    close(fd);

    // ...
    // 省略一些代码
    // ...

    return YES;
}

_read函数首先打开指定的Mach-O文件,然后读取Mach-O文件头。如果文件头读取成功,_read函数会继续解析Mach-O文件中的Objective-C类和协议信息。

imp_lookup函数

imp_lookup函数是ObjC运行时用来查找指定方法的实现的函数。imp_lookup函数的调用过程如下:

- (IMP)imp_lookup:(SEL)aSelector inClass:(Class)aClass
{
    // ...
    // 省略一些代码
    // ...

    IMP imp = NULL;

    // ...
    // 省略一些代码
    // ...

    return imp;
}

从代码中可以看到,imp_lookup函数首先尝试从aClass的父类中查找aSelector方法的实现。如果在父类中找不到,则会尝试从aClass的元类中查找。如果在元类中也找不到,则会尝试从aClass的实现类中查找。如果在实现类中也找不到,则会尝试从aClass的分类中查找。如果在分类中也找不到,则会返回NULL。

load_images函数

load_images函数是ObjC运行时用来加载所有Objective-C类的函数。load_images函数的调用过程如下:

- (void)load_images
{
    // ...
    // 省略一些代码
    // ...

    struct mach_header_64 header;

    // ...
    // 省略一些代码
    // ...

    unsigned int count = 0;
    const char **images = _dyld_image_paths(&count);

    // ...
    // 省略一些代码
    // ...

    BOOL success = [self readImages:images count:count];

    // ...
    // 省略一些代码
    // ...
}

从代码中可以看到,load_images函数首先获取所有需要加载的Mach-O文件的路径,然后调用readImages函数来加载这些Mach-O文件中的Objective-C类和协议的信息。

_objc_enter_runtime函数

_objc_enter_runtime函数是ObjC运行时用来进入运行时的函数。_objc_enter_runtime函数的调用过程如下:

- (void)_objc_enter_runtime
{
    // ...
    // 省略一些代码
    // ...

    // 注册Objective-C类和协议
    _class_register(&_objc_class);
    _class_register(&_objc_protocol);

    // ...
    // 省略一些代码
    // ...
}

从代码中可以看到,_objc_enter_runtime函数首先将Objective-C类和协议注册到运行时。然后,_objc_enter_runtime函数会执行一些其他初始化操作,例如初始化Objective-C运行时的垃圾回收器。

总结

本文对ObjC源码中read_images函数的下半部分代码流程进行了分析。我们探讨了_read函数、imp_lookup函数、load_images函数和_objc_enter_runtime函数等关键函数的实现和功能。通过对这些函数的分析,我们对iOS类加载流程有了更深入的了解。