Obj-C运行时系统

Objective-C 运行时系统

Objective-C 会在运行时执行许多其他语言在编译或链接时执行的常规操作,如确定类型和方法解析。这些处理会带来额外的开销,Objective-C 通过缓存来节约这些开销。

选择器

在 Objective-C 的对象消息传递中,通过被称为 选择器 的字符串来指明调用对象的哪个方法。选择器是一种分为多个段的文本字符串,与方法的声明对应,例如:

1
分段1:分段2:分段3

如果创建了一个叫做 calculator 的变量,想调用它的方法,就要在接受器对象(calculator)后跟带输入参数的选择器:

1
[calculator sumAddend1:25 addend2:10];

选择器类型(SEL)是用于在编译源代码时替换选择器值的唯一标识符,Objective-C 运行时系统会保证每个选择器标识符的唯一性。可以用关键字 @selector 来创建 SEL 类型的变量。

1
SEL myMethod = @selector(myMethod);

也可以用 Foundation 框架中的 NSSelectorFromString 在运行时创建选择器:

1
SEL myMethod = NSSelectorFromString(@"myMethod");

方法签名

方法签名定义了输入参数的数据类型和返回值。编译器会把 [接收器 消息] 形式的对象消息转换为生命中带有方法签名的 C 函数调用语句。因此,为了生成对象消息传递代码,编译器需要获得选择器值和方法签名。从对象消息表达式中提取选择器很容易,但怎么提取方法签名呢?由于接受器和接受器的方法是在程序运行时确定的,因此编译时没有办法知道怎样的数据类型能和要调用的方法对应起来。所以编译器只能根据已知的方法声明进行猜测。如果找不到方法签名,或者方法签名与运行时实际的执行方法不匹配,就会导致从编译器警告到运行时错误的各种问题产生。

动态类型

运行时系统通过动态类型功能,可以在运行时决定对象的类型。Objective-C 既支持静态类型,又支持动态类型。使用静态类型时,能在编译期检查类型,而使用动态类型时,就只能在运行时检查类型了。Objective-C 通过 id 类型来支持动态类型。id 类型的变量可以存储任何数据类型的对象。

Objective-C 还为运行时的对象内省(如检查对象属于哪个类)提供了 API。通过内省我们可以在运行时检查对象类型,从而确定对象是否能够执行特定的操作。

动态绑定

动态绑定是指在运行时将消息和方法对应起来的过程。在运行时发送消息前,消息和接收消息的对象不会对应。由于许多接受器可能会实现相同的方法,所以调用方法的方式会动态变化,因而这也实现了 OOP 的多态。

动态方法决议

使用动态方法决议能够以动态的方式实现方法。可以通过重写 NSObject 的 resolveInstanceMethodresolveClassMethod 方法来动态实现实例方法和类方法。

可以通过 class_addMethod() API 来将函数添加到类中,需要先 #import <objc/runtime.h>

动态加载

通过动态加载功能,可以在需要时加载可执行代码和源代码,而不必在程序启动时就加载所有的组件。这种 lazy loading 方式可以提高程序的性能和可拓展性。可以通过 NSBundle 来动态加载。

内省

通过系统提供的 API 可以动态查询与方法有关的信息,并测试对象的继承性、行为和一致性信息。

运行时系统的组成结构

Objective-C 的运行时系统由以下两部分组成:编译器和运行时系统库。

编译器

当编译器解析到使用了上述动态特性的 Objective-C 源代码时,会使用适当的运行时系统库函数来生成可执行代码。

当编译器解析对象消息时,会生成调用运行时系统库函数 objc_msgSend() 中的代码,该函数以接受器、选择器和消息传递的参数一起作为参数。每条消息都是动态处理的,因此接受器类型和方法的实际实现都是在运行时决定的。

当编译器解析含有类定义和对象的代码时,会生成相应的运行时数据结构。所有运行时类型都以 isa 指针开头。

运行时系统库

运行时系统库也提供了 C 语言的公共 API 供我们使用,这些 API 在 runtime.h 中声明。比如,可以动态的创建一个类。

1
Class dynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);

当向对象发送消息时,运行时系统会通过自定义代码中的类方法缓存或虚函数表来查找类的方法。虚函数表也叫分派表,是一种动态绑定支持机制。最近调用过的方法的指针会被缓存起来,以优化性能。

运行时系统库定义的方法数据类型 objc_method 定义如下:

1
2
3
4
5
6
struct objc_method {
SEL method_name;
char * method_types;
IMP method_imp;
}
typedef objc_method Method;

其中,IMP 类型的变量用来提供方法的地址。

方法查询逻辑如下:

etho

类方法是如何寻找的呢?运行时系统是通过元类(metaclass)来实现的。每个类都拥有一个独一无二的元类(可认为元类是类的类),元类向普通的类一样,也通过父类指针指向父类的元类。基类的元类会让它的父类指针指向基类本身。

# Obj-C
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×