本文翻译自苹果官方开发文档 Objective-C Runtime Programming Guide.
介绍
重要: 该文档已经不再更新. 需要了解 Apple SDKs 的最新信息, 请访问 文档页面. |
---|
Objective-C 语言尽可能地从编译时和链接时到 runtime (运行时)推迟决定。只要可能的话,它都会动态地完成任务。这意味着这个语言不仅需要一个编译器(compiler),而且需要一个 runtime 系统(runtime system)来执行编译过的代码。Runtime 系统对于 Objective-C 语言来说表现地像是一种操作系统;这也是这个语言运作的原因(it’s what makes the language work)。
该文档介绍了NSObject
类以及 Objective-C 程序如何与 runtime 系统交互。 特别是,它检查了在 runtime 动态加载新的类和转发消息给其他对象的范例(paradigms)。它还提供了一些信息,这些信息是关于当你的程序正在跑的时候你如何找到对象的信息。
你应该阅读该文档以增加一些关于 Objective-C runtime 系统如何工作以及你如何使用它的理解。但是在通常情况下,你应该没有理由需要了解和理解这个材料来写 Cocoa 应用。
该文档的组织架构
该文档有如下章节:
-
Runtime 的版本和平台
-
与 Runtime 交互
-
消息
-
动态方法解析
-
消息转发
-
类型编码
-
声明的属性
更多资料
Objective-C Runtime Reference 描述 Objective-C runtime 的支持库的数据接口和函数。你的程序可以用这些接口来和 Objective-C runtime 系统交互。例如,你可以添加类或者方法,或者获取到所有已经加载的类(loaded class)的类定义列表。
Programming with Objective-C 描述 Objective-C 语言
Objective-C Release Notes 描述在最近的 OS X 发布版本中的 Objective-C runtime 的一些变化。
Runtime 的版本和平台
在不同的平台上有不同的 Objective-C runtime 版本。
Legacy(遗产)版本和 Modern(现代) 版本
Objective-C runtime 有两个版本 — “modern” 和 “legacy”。Modern 版本是伴随 Objective-C 2.0 引进的,包含一些新的特性。Legacy 版本的 runtime 的编程接口在 Objective-C Runtime Reference 中描述。
Modern runtime 最重要的新特性是实例变量是“非易碎的(non-fragile)”:
-
在 legacy runtime 中,如果你改变一个类中实例变量的布局(layout),那么你必须重新编译继承自它的类。
-
在 modern runtime 中,如果你改变一个类中实例变量的布局(layout),你不需要重新编译继承自它的类。
另外,modern runtime 支持对声明的属性进行实例变量的合成 (见 The Objective-C Programming Language 中的 Declared Properties).
平台
iPhone 应用和OS X v10.5 或更近的版本的64位程序使用 modern 版本的 runtime。
其他的程序(OS X 桌面系统的32位程序)使用 legacy 版本的 runtime。
和 Runtime 交互
Objective-C 程序在三个不同的层级上和 runtime 系统进行交互:通过 Objective-C 源码;使用 Foundation 框架中的 NSObject
类定义的方法;通过直接调用 runtime 函数。
Objective-C 源码
在大多数情况下,runtime 系统在背后自动工作。你只需要编写和编译 Objective-C 源码就可以使用它。
当你编译包含 Objective-C 的类和方法的代码时,编译器会创建实现该语言动态特性的数据结构和函数调用。数据结构捕获在类和分类定义和协议声明中找到的信息;他们包含类和协议对象,这在 Defining a Class 和 The Objective-C Programming Language 中的 Protocols 中有讨论,以及方法选择器,实例变量模板,和从源码中提取中的其他信息。最重要的 runtime 函数是发送消息的函数,如 Messaging 中所述。它由源代码消息表达式调用。
NSObject 方法
Cocoa 中的大多数对象都是 NSObject
的子类,所以大多数对象继承它定义的方法。(值得注意的例外是 NSProxy
类;更多信息见 Message Forwarding 。)因此,它的方法建立了每个实例和每个类对象固有的行为。然而,在个别情况下,NSObject
只是定义了一个如何做某件事情的模板;它不会自己提供所有必要的代码。
例如,NSObject
类定义了一个 description
实例方法,这个方法返回一个用于描述该类的内容的字符串。它主要用于调试— GDB 的 print-object
命令输出这个方法返回的字符串。NSObject
对这个方法的实现不知道这个类包含什么,所以它返回一个带有对象名称和地址的字符串。NSObject
的子类会实现这个方法以返回更多细节。例如,Foundation 类 NSArray
返回一个它所包含的所有对象的列表。
NSObject
的一些方法简单地查询 runtime 系统的信息。这些方法允许对象执行内省(perform introspection)。这类方法的一些例子如 类
方法(class
method),要求对象识别它所属的类;isKindOfClass:
和 isMemberOfClass:
,检测对象在继承层级的位置;respondsToSelector:
,表示对象是否可以接受特定的消息;conformsToProtocol:
,表示对象是否声称实现特定协议中定义的方法;methodForSelector:
,提供方法的实现的地址。这类方法使对象能够自省内省(introspect about itself)。
Runtime 函数
Runtime 系统是一个动态共享库,它有一个公共的接口,由位于 /usr/include/objc
目录下的头文件中的一组函数和数据结构组成。当你编写Objective-C 代码时,其中很多函数可以用普通的C(plain C)来复制编译器所做的事。Others form the basis for functionality exported through the methods of the NSObject
class. 这些函数使为 runtime 系统开发其它的接口和生产增强开发环境的工具成为可能;这些函数在用 Objective-C 编程时并不是必须的。然而,在编写 Objective-C 程序时,一些 runtime 函数有时可能会很有用。所有这些函数都收录在文档 Objective-C Runtime Reference 中。
消息发送(Messaging)
此章节描述如何将消息表达式如何被转换为 objc_msgSend 函数调用,以及如何通过名称引用方法。然后解释了如何运用 objc_msgSend
,以及如何规避动态绑定—如果需要的话。
The objc_msgSend 函数
In Objective-C, messages aren’t bound to method implementations until runtime. The compiler converts a message expression,
在 Objective-C 中,消息知道 runtime 才会被绑定到方法实现。编译器会将消息表达式,
[receiver message]
转换为消息发送函数 objc_msgSend 的调用。这个函数将接收者(receiver)和消息(message)中提到的方法的名称—也就是方法选择器(method selector)——作为它的两个主要参数:
objc_msgSend(receiver, selector)
任何在消息中传递的其他参数也会传递给 objc_msgSend
:
objc_msgSend(receiver, selector, arg1, arg2, ...)
消息发送函数可以完成动态绑定所需的一切:
-
它首先找到选择器引用的程序(方法的实现)。由于不同的类可以实现相同的方法,因此它根据接收者(receiver)的类来找到准确的程序。
-
然后它调用程序,将接收对象(指向对象数据的指针)传递给它,同时传递这个方法指定的所有参数。
-
最后,它将程序的返回值作为它自己的返回值传递。
注意:编译器生成消息发送函数的调用。你永远不要在你写的代码中去调用它 |
---|
消息发送最重要的点在于编译器为每个类和对象构建的结构体。每个类结构体都包含以下两个要素:
-
一个指向父类的指针。
-
类派遣表(dispatch table)。这个表具有将方法选择器和它们标识的方法的特定于类的地址相关联的条目(读不懂,原文:This table has entries that associate method selectors with the class-specific addresses of the methods they identify)。
setOrigin::
方法的选择器与setOrigin::
的地址(实现的程序)相关联,display
方法的选择器与display
的地址相关联,等等。
创建新对象时,将为它分配内存,并初始化其实例变量。其中,对象的第一个变量是指向它的类结构体的指针。这个指针,又称为 isa
,使对象可以访问它的类,并通过该类访问继承自的所有类。
注意:虽然不是严格意义上的语言的一部分,但是对象需要 isa 指针才能使用 Objective-C runtime 系统。在结构定义的任何字段中,对象需要与struct objc_object (在objc / objc.h 中定义)“等效(equivalent)”。但是,你很少(如果有的话)需要创建自己的根对象(root object),并且继承自 NSObject 或 NSProxy 的对象会自动具有 isa 变量。 |
---|
类和对象结构体的这些要素如下图所示
当消息被发送给对象时,消息发送函数跟随对象的
isa
指针找到类结构体,并在结构体的派遣表中查找方法选择器。如果不能找到选择器,objc_msgSend
就跟随指针找到父类,然后尝试在派遣表中找到选择器。连续的查找失败会使 objc_msgSend
沿着类的继承层级向上直到 NSObject
类。一旦定位到选择器,消息发送函数就会调用表中的方法,并且传入对象的数据结构(参数?)。
这就是在 runtime 下方法的实现被选择的方式—或者用面向对象编程的术语来说,方法被动态地绑定到消息。
TO BE CONTINUED...
网友评论