Runtime总结

Class

文件:objc.h

Class 是一个 objc_class 类型的结构体指针,也就是类。idobjc_object 类型的指针。如下:

1
2
3
4
5
6
7
8
9
10
11
12
#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

objc_class 定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; // 元类

#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 指向父类,如果该类是 NSObject 或者是 NSProxy,那么 super_class 为 nil。
const char *name OBJC2_UNAVAILABLE; // 类名
long version OBJC2_UNAVAILABLE; // 类的版本信息(默认为0,可通过 class_setVersion、class_getVersion)进行修改、读取
long info OBJC2_UNAVAILABLE; // 类信息(运行时期使用的一些位标识,如CLS_CLASS (0x1L) 表示该类为普通 class,其中包含实例方法和变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法)
long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小(包括从父类继承下来的实例变量)
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类成员变量地址
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法地址列表,与 info 的一些标志位有关。(如CLS_CLASS (0x1L),则存储实例方法,如CLS_META (0x2L),则存储类方法;)
struct objc_cache *cache OBJC2_UNAVAILABLE; // 缓存方法表,用来提高方法表的查找速度
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 该类声明遵守的协议的列表
#endif

} OBJC2_UNAVAILABLE;

由此可见,类也是一个对象。也就是说类和对象都是对象,分别被称为 类对象实例对象

isaobjc_object(实例对象)中的 isa 指针指向类的结构体称为 class(也就是该对象所属的类)。其中存放着普通成员变量与动态方法实例方法(“-“开头)。objc_class 中的 isa 指针指向类结构体称之为 metaclass,其中存放着 static 类型的成员变量与 static 类型的方法(“+” 开头的方法)。

一图胜千言:

所有实例对象的 isa 指针都指向它所属的类,所属的类的 isa 指针指向其 metaclass。所有的 metaclass 都指向其之神。root metaclass是通过继承根类产生的,与根 class 结构体成员一致,不同的是root metaclassisa 指针指向自身。

Objective-C 中的元素

SEL

SELselector 在 Objective-C 中的表示类型。selector 可以理解为区别方法的 ID。

1
typedef struct objc_selector *SEL;

其定义如下:

1
2
3
4
struct objc_selector {
char *name; OBJC2_UNAVAILABLE; // 名称
char *types; OBJC2_UNAVAILABLE; // 类型
};

IMP

IMP 是编译器生成的函数指针。该类型决定调用具体函数的实现。

其定义如下:

1
2
3
4
5
6
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif

Method

Method 代表类中的某个方法的类型。

1
typedef struct objc_method *Method;

其定义如下:

1
2
3
4
5
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE; // 方法类型
IMP method_imp OBJC2_UNAVAILABLE; // 方法实现
}

Ivar

Ivar 代表类中实例变量的类型。

1
typedef struct objc_ivar *Ivar;

其定义如下:

1
2
3
4
5
6
7
8
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; // 变量名
char *ivar_type OBJC2_UNAVAILABLE; // 变量类型
int ivar_offset OBJC2_UNAVAILABLE; // 基地址偏移字节
#ifdef __LP64__
int space OBJC2_UNAVAILABLE; // 占用空间
#endif
}

objc_property_t

objc_property_t 是内置类型属性,其定义如下:

1
typedef struct objc_property *objc_property_t;

还有一个关联属性 objc_property_attribute_t 类型,它是属性的 attribute,包含属性名称、属性编码类型、原子类型/非原子类型等。其定义如下:

1
2
3
4
typedef struct {
const char *name; // 名称
const char *value; // 值
} objc_property_attribute_t;

Cache

Cache:缓存方法表

1
typedef struct objc_cache *Cache

其定义如下:

1
2
3
4
5
struct objc_cache {
unsigned int mask OBJC2_UNAVAILABLE; // 指定分配的 cache buckets,在方法查找中,Runtime使用这个字段确定数组的索引位置。
unsigned int occupied OBJC2_UNAVAILABLE; // 实际占用cache buckets的总数。(bucket 等于 NULL,表示缓存没有被占用,被占用的 bucket 可能是不连续的额,这个数组可能会随着时间而增长)
Method buckets[1] OBJC2_UNAVAILABLE; // objc_msgSend 发送消息的函数。
};

Catagory

Catagory:可以动态为已经存在的类添加新的方法。

1
typedef struct objc_category *Category;

其定义如下:

1
2
3
4
5
6
7
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 类别名称
char *class_name OBJC2_UNAVAILABLE; // 类名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议列表
}

消息传递

在 Objective-C 中,调用某个方法,相当于发送一条消息。会调用 runtime 模块中的 objc_msgSend 函数调用,比如 [self someMethod]; 方法,会转换成 objc_msgSend(self, someMethod)

消息调用类型还包括以下几种:

  • objc_msgSend_stret:如果带发送的消息返回结构体,可交由此函数处理。
  • objc_msgSend_fpret:消息返回浮点数,
  • objc_msgSend_Super:给父类发送消息,如 [super message:parameter]

更多解释可以查看这里

消息转发

第一步:通过 resolveInstanceMethod: 方法决定是否能在本类中动态添加方法,返回 YES 可以通过 class_addMethod 来动态添加方法,消息得到处理。如果返回 NO,则会启动将消息转发给其他对象。
第二步:通过 forwardingTargetForSelector:,当前接收者有第二次机会处理这个未知的消息,runtime 会询问能否将消息转给其他的接收者来处理。如果返回某个对象则会调用对象的方法,然后结束。反之返回 nil,则进入第三步。
第三步:通过 methodSignatureForSelector:方法签名,如果返回 nil,则消息无法处理。如果返回 methodSignature 则进入下一步。
第四步:通过 forwardInvocation:方法,可以通过 NSInvocation 对象做很多处理,比如修改方法的实现,修改响应对象等。如果方法调用成功,则结束。如果失败,则调用 doesNotRecognizeSelector 方法,如果没有实现这个方法,则程序会崩溃。

完整流程图:

地方