iOS运行时Runtime

基础概念

SEL

SEL是方法选择器,即方法的ID。结构体如下,源码中没有objc_selector具体的定义。
但其实就是映射到方法的C字符串。不同类的同名方法具有相同的选择器,方法名相同单变量类型不同也会导致它们具有相同的选择器

1
2
3
// An opaque type that represents a method selector.

typedef struct objc_selector *SEL;

id

id的结构体定义如下:

1
typedef struct objc_object *id;
1
2
3
4
5
6
7
8
9
10
11
12
struct objc_object {
private:
isa_t isa;

public:

// ISA() assumes this is NOT a tagged pointer object
Class ISA();

// getIsa() allows this to be a tagged pointer object
Class getIsa();
}

objc_object结构体包含一个isa_t指针,isa_t是一个联合体,根据isa指针可以找到对象所属的类,isa还涉及到 tagged pointer 的相关知识点。

Class

Class 是一个指向 objc_class结构体的指针
objc_class继承自 objc_object结构体,objc_class的很多方法都基于它的几个成员实现的。

1
typedef struct objc_class *Class;
1
2
3
4
5
6
7
8
9
10
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

class_rw_t *data() {
return bits.data();
}
};

为了处理类与对象的关系,Runtime创造了元类(Meta Class,类对象所属的类型就是元类,它用来表示类本身所具备的元数据。每个类只有一个类对象,每个类对象只对应一个元类

实例对象的isa指针指向类对象的,类对象的isa指针指向元类
根元类的isa指针指向的是自己,而超类指向的NSObjectNSObject的超类的为nil,也就是NSObject没有超类

cache_t

cache_t 定义如下,方法缓存列表

1
2
3
4
5
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
}

buckets是一个hask表,存储了调用过的方法体IMP。

1
2
3
4
5
6

struct bucket_t {
private:
cache_key_t _key;
IMP _imp;
}

cache为方法调用进行了性能优化,每次方法调用了先从cache中查找,命中就返回方法体IMP,否则再从isa指向的方法列表遍历查找能够响应消息的方法

class_data_bits_t

class_data_bits_t结构体定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct class_data_bits_t {

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

private:
bool getBit(uintptr_t bit)
{
return bits & bit;
}

void setBits(uintptr_t set)
{
uintptr_t oldBits;
uintptr_t newBits;
do {
oldBits = LoadExclusive(&bits);
newBits = updateFastAlloc(oldBits | set, set);
} while (!StoreReleaseExclusive(&bits, oldBits, newBits));
}

void clearBits(uintptr_t clear)
{
uintptr_t oldBits;
uintptr_t newBits;
do {
oldBits = LoadExclusive(&bits);
newBits = updateFastAlloc(oldBits & ~clear, clear);
} while (!StoreReleaseExclusive(&bits, oldBits, newBits));
}

public:

class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}

class_data_bits_t 包含了一个 bits,这个指针跟不同的FAST_前缀的flag掩码做按位与操作,获取不同的数据

FAST_前缀的flag掩码

1
2
3
4
5
6
7
8
9
// class is a Swift class
#define FAST_IS_SWIFT (1UL<<0)
// class or superclass has default retain/release/autorelease/retainCount/
// _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
#define FAST_HAS_DEFAULT_RR (1UL<<1)
// class's instances requires raw isa
#define FAST_REQUIRES_RAW_ISA (1UL<<2)
// data pointer
#define FAST_DATA_MASK 0x00007ffffffffff8UL

bits中其他的Fast_都是存储了相关标志位bool值,FAST_DATA_MASK 则存储了指向class_rw_t的指针

1
2
3
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}

class_ro_t

objc_class 包含了 class_data_bits_tclass_data_bits_t存储了 class_rw_t指针, class_rw_t有包含了了 class_ro_t

class_ro_t的结构体定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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_ro_tmethod_list_tproperty_list_tproperty_list_t都继承自entsize_list_ttprotocol_list_t 存储了 protocol_ref_t列表(也就是protocol_t指针列表)。class_ro_t 存储的大多都是编译时信息就确定的信息

class_rw_t

class_rw_t结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
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;
}

class_rw_t 提供了运行时类拓展的能力,而class_ro_t存储的大多是编译时就确定的信息。虽然二者都包含了类的方法、属性以及协议等信息,但列表的实现方式不尽相同。
class_rw_t中的 method_array_tproperty_array_tprotocol_array_t 都继承自list_array_tt,list_array_tt真正的为运行时提供了拓展能力,因为list_array_tt可以存储list指针,内容有三种:

  1. entsize_list_tt指针
  2. entsize_list_tt指针数组

realizeClass

Category

Method

IMP

Ivar

objc_property_t

protocol_t

消息发送

objc_msgSend流程

  1. 检测这个selector是否要忽略
  2. 检测target是否为nil
  3. cache中查找对应的函数
  4. cache找不到,就从方法分发表中查找
  5. 方法分发表中还找不到就到超类中查找,直到NSObject
  6. 如果还找不到就进入动态方法解析

动态方法解析

消息转发

#健壮的实例变量 (Non Fragile ivars)

Objective-C Associated Objects

Method Swizzling 方法替换

总结