Objective-C Runtime 初识(一)

By | 2016-02-22

平时也有了解一些 Runtime 相关的知识,但都是只处于知道阶段没有详细了解过,最近没有那么忙了准备抽个时间来边学边记录一下

初识

Runtime 字面意思运行时,是一个将 C 语言转化为面向对象语言的扩展,是 Objective-C 被称之为动态语言的核心。

在 C 语言中函数的调用在编译期就已经确定,而在 Objective-C 中在编译期内无法确定具体调用哪个函数,只有在真正运行的时候才确定调用。

本篇博客具体记录基于 objc2.0 对象在 Runtime 中的定义。

objc_class

objc2.0 之后的 objc_class 定义如下

typedef struct objc_class *Class;
typedef struct objc_object *id;

struct objc_object {
private:
    isa_t isa;
}

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

从以上代码可以得出:

  • Objective-C 的对象都是 C 语言结构体
  • id 是指向 struct objc_object 的一个指针
  • class 是指向 objc_class 的指针
  • objc_class 继承于 objc_object

isa & superclass

isa 是一个 isa_t 的结构体,它包含了当前对象指向的类的信息。

  • 对象中的 isa 指向它的类对象
  • 类对象中的 isa 指向它的元类对象

superclass 指向它的父类对象。

对应关系图如下:

关系图

类对象的 isa 指向一个 meta class 元类,所有元类的 isa 都指向了 Root class (meta) ,而 Root class (meta)Root class (class) 的子类,即 NSObject / NSProxy 的子类。

cache

cache 用于方法性能优化,每次发送消息时,优先在 cache 中查找,查找不到的情况下去类和它的父类方法列表中查找,找不到的情况下消息转发,找到之后会将方法缓存在 cache 中。

找到 cache_t 结构体的具体实现:

struct cache_t {  
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
}

typedef unsigned int uint32_t;  
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits

typedef unsigned long  uintptr_t;  
typedef uintptr_t cache_key_t;

struct bucket_t {  
private:  
    cache_key_t _key;
    IMP _imp;
}
  • _buckets 是一个散列表,用来做方法缓存
  • _mask 分配可以缓存的 bucket_t 总数
  • _occupied 实际占用缓存的 bucket_t 个数

bits

class_data_bits_t 源码实现如下

struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
}

struct class_rw_t {  
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
}

struct class_ro_t {  
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;

    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

class_rw_t 中存放类的属性列表、方法列表、遵循的协议列表、只读的 class_ro_t 等,它提供了运行时对类进行扩展的能力。

class_ro_t 存储的信息基本都是编译时就已经可以确定的信息,在编译之后不做修改。

类初始化之前 objc_class->data() 返回的指针指向的是一个 class_ro_t 结构体,初始化时类调用 realizeClass 静态方法,开辟一个 class_rw_t 的空间,并把 class_ro_t 指针赋值给 class_rw_t->ro ,具体实现细节有时间在仔细研究研究。。

References: