张国文
摘 要:通过笔者多年i0S应用开发的经验,就i0S应用开发的运行时机制做一个浅析,从运行时机制是什么,到运行时机制的基本原理,与运行时交互的方式,及运行时中的数据结构,为希望从事iOS开发的朋友打开一扇门。
关键词:RunTime;运行时机制;动态调用
RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
1 怎么理解OC是动态语言,Runtime又是什么?
静态语言:如C语言,编译阶段就要决定调用哪个函数,如果函数未实现就会编译报错。
动态语言:如OC语言,编译阶段并不能决定真正调用哪个函数,只要函数声明过即使没有实现也不会报错。
我们常说OC是一门动态语言,就是因为它总是把一些决定性的工作从编译阶段推迟到运行时阶段。OC代码的运行不仅需要编译器,还需要运行时系统(Runtime Sytem)来执行编译后的代码。
Runtime是一套底层纯C语言API,OC代码最终都会被编译器转化为运行时代码,通过消息机制决定函数调用方式,这也是OC作为动态语言使用的基础。
2 理解消息机制的基本原理
OC的方法调用都是类似[receiver selector]的形式,其实每次都是一个运行时消息发送过程。
第一步:编译阶段
[receiver selector]方法被编译器转化,分为两种情况:
(1)不带参数的方法被编译为:objc_msgSend(receiver,selector)
(2)带参数的方法被编译为:objc_msgSend(recevier,selector,org1,org2,…)
第二步:运行时阶段
消息接收者recever寻找对应的selector,也分为两种情况:
(1)接收者能找到对应的selector,直接执行接收receiver对象的selector方法。
(2)接收者找不到对应的selector,消息被转发或者临时向接收者添加这个selector对应的实现内容,否则崩溃。
3 与Runtime的交互
Runtime的官方文档中将OC与Runtime的交互划分三种层次:OC源代码,NSObject方法,Runtime 函数。这其实也是按照与Runtime交互程度从低到高排序的三种方式。
3.1 OC源代码(Objec-C Source Code)
我们已经说过,OC代码会在编译阶段被编译器转化。OC中的类、方法和协议等在Runtime中都由一些数据结构来定义。所以,我们平时直接使用OC编写代码,其实这已经是在和Runtime进行交互了,只不过这个过程对于我们来说是无感的。
3.2 NSObject方法(NSObject Methods)
Runtime的最大特征就是实现了OC语言的动态特性。作为大部分Objective-C类继承体系的根类的NSObject,其本身就具有了一些非常具有运行时动态特性的方法,比如respondsToSelector:方法可以检查在代码运行阶段当前对象是否能响应指定的消息,所以使用这些方法也算是一种与Runtme的交互方式。
3.3 使用Runtime函数(Runtime Functions)
Runtime系统是一个由一系列函数和数据结构组成,具有公共接口的动态共享库。頭文件存放于/usr/include/objc目录下。在我们工程代码里引用Runtime的头文件,同样能够实现类似OC代码的效果。
4 深入理解Rutime消息发送
我们在分析了OC语言对应的底层C结构之后,现在可以进一步理解运行时的消息发送机制。先前讲到,OC调用方法被编译转化为如下的形式:
id _Nullable objc_msgSend(id _Nullable self,SEL _Nonnull op,…)
其实,除了常见的objc_msgSend,消息发送的方法还有objc_msgSend_stret,objc_msgSendSuper,objc_msgSendSuper_stret等,如果消息传递给超类就使用带有super的方法,如果返回值是结构体而不是简单值就使用带有stret的值。
5 最后总结
通过iOS的runtime机制,可以更加灵活的进行开发和调试。
参考文献
[1]Matt Galloway [Effective Objective-C 2.0] 2014.
[2]Vandad Nahavandipoor [iOS 7 Programming Cookbook]2013.