Runtime是Objective-C语言的重要特性之一,也是Objective-C之所以能够成为动态语言的支柱。深入理解Runtime,有助于整体把握Objective-C语言的运行机制和原理。本篇文章主要内容有:
- Runtime的概念
- Runtime的相关术语解释
- 消息机制
Runtime的概念
Objective-C是一门动态语言,动态语言和静态语言的区别在于将很多编译期的工作放在运行期做,实现了非常大的灵活性。Runtime就是保证Objective-C语言动态性的库,这个库底层由C语言实现,并且引入了多种结构体以支持OC中的类、对象等。
Runtime的相关术语解释
OC中对象和类的继承关系如下:
实例对象的isa指针指向派生该对象的类,普通类的isa指针指向派生该类的元类,元类的isa指针指向根元类,根源类的isa指针指向自身。根元类由根类派生。所以,类的结构体里包含了众多信息:isa、父类、类名称、版本、信息等等:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
IMP
/// 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
IMP是指向函数实现的指针,每一个函数对应一个IMP。
SEL
struct objc_selector {
char *name; OBJC2_UNAVAILABLE;
char *types; OBJC2_UNAVAILABLE;
};
selector是用来区别不同方法的标识。
其他类型结构体例如 objc_ivar、objc_method、objc_property、objc_cache、objc_category在源码中都有清楚的表示,并且比较容易理解,这里就不再列出。
消息机制
静态语言中,实例对象调用方法是在编译期就能够确定执行内容的,而OC中调用方法实际是对某个对象发送消息:例如 [anObject method] 表示对实例对象anObject发送了一条消息method,具体anObject是否执行method中的代码不确定。消息机制包括消息发送和消息转发两个过程。
消息发送
消息发送是通过selector查找IMP,找到后执行对应代码的过程。用流程图表示为:
消息转发
消息转发是在IMP找不到的情况下,后续执行的流程。
(1) resolveInstanceMethod返回YES,则消息处理放在这个方法中,可以通过添加方法实现。返回NO,则进入下一步。
(2)如果返回一个备选对象,则会调用该对象的方法,如果返回nil,则进入下一步。
(3)如果返回methodSignature,则进入下一步,如果返回nil,消息无法处理,Runtime会发出doesNotRecognizeSelector消息,程序崩溃。
(4)在forwardInvocation中,我们可以修改响应对象,修改方法实现等。
概括来说,消息转发是对消息发送时的异常情况做的补救措施。消息机制的源码分析(包括c语言和汇编)可以参照这篇博客,总结得很详细很好。