什么是runtime
依照苹果文档的说法,runtime
是:
The Objective-C language defers as many decisions as it can from compile time and link time to runtime.
(尽量将决定放到运行的时候,而不是在编译和链接过程)
如何理解这段话呢,我们首先要知道,一段代码从写完到最终执行的过程中发生了什么。
从代码到可执行文件的过程
这是《深入理解计算机系统(第2版)》里面的一张截图:
主要过程我们可以简化成三个:
- 编译
- 链接
- 运行
编译:将代码转换成底层可执行的语言(如汇编),简单来讲,就是把你能看懂的语言,转换成系统底层可以看懂的东西,这中间通常会有优化,先预处理,再编译。
链接:在编译的过程中,如果有调用其他的类的方法等,是不会检查或者报警的,编译的时候会默认你已经实现了。而链接就是去检查调用的方法或者类等是否确实存在。
运行:执行最终的可执行文件
如果是普通的C语言代码,我们使用的是传统的编译运行,那么一个函数的执行内容,在编译阶段其实就确定了,执行的时候只要去执行对应的内存地址的程序就好。
而在runtime
中,编译阶段只能确定最终要执行的函数名,但是具体执行的时候,执行的是什么程序,是在运行的时候才能确定,大大增加了程序的灵活性。
Objective-C runtime介绍
简介
Objective-C是一门运行时语言,这意味着代码执行可以更加灵活:我们动态的创建一个新的类,还可以转发消息给其他的消息。(消息转发是runtime的一个重要组成部分,后面会介绍)。
这种特性要求Objective-C语言会尽可能把决定从编译和链接的阶段延迟到运行时(runtime)
阶段,因此Objective-C不仅有一个编译器,还有一个runtime
系统来执行被编译过的代码。
版本和平台
runtime是有个两个版本的: legacy 、 modern
在Objective-C 1.0使用的是legacy,在2.0使用的是modern。这里简单介绍下区别:
- 在legacy runtime,如果你改变了实例变量的设计,需要重新编译它的子类。支持 32bit的OS X 程序
- 在modern runtime,如果你改变了实例变量的设计,不需要重新编译它的子类。支持iphone程序和OS X10.5之后的64bit程序
因为legacy是如此的古老,我们基本可以忽略legacy版本。
Runtime交互
有三种方式可以使用Runtime:
- Objective-C 源代码
- NSObject 方法
- Runtime 方法
Objective-C 源代码
一般来说,runtime都是默默地在后台运行工作,我们只是写Objective-C源代码,就使用了runtime。当我们编译包含Objective-C的类和方法时,编译器就会生成包含runtime特性的数据结构和方法。数据结构中包含了从类、category、协议中定义的的信息,如:selector、变量等等。主要的runtime方法是发送信息的方法Message,在下一章节会讲到。
NSObject方法
Cocoa中大部分的类都是继承自NSObejct
,所以他们都会集成了NSObject
定义的方法。(值得注意的例外是NSProxy
类)因此NSObject
的方法就决定了其他类的行为。(当然,在少数情况下,这样说并不正确,在这些情况下,NSObject
会仅仅定义了方法,没有实现必须的代码。比如description
,如果子类没有重写,那么会返回类名和地址,这是因为NSObject
没法获得更多的信息。)
一些NSObject
类的方法可以直接查询runtime
系统的信息,从而获得自身的信息。比如class
,isKindeOfClass
,respondsToSelector
等方法。
Runtime方法
Runtime系统是一个共享的library,包含了许多方法和数据结构,地址在/usr/include/objc
。其中的很多方法,允许你使用C来重写编译行为,其他的方法是通过NSObject
类使用。有了这些方法,我们就可以为runtime
系统增加接口,或者工具。
Message
先抛出来一个问题,这句话代表什么?
[receiver message]
receiver执行message函数? 是这个作用,但是more than that,等价于这行代码<