版本和平台

Runtime System对于Objective-C来说就好比是它的操作系统,或者说是运行的支撑平台,它使得Objective-C代码能够按照既定的语言特性跑起来。相对于C/C++来说,Objective-C尽可能地把一些动作推迟到运行时来执行,即尽可能动态地做事情。因此,它不仅需要一个编译器,还需要一个运行时环境来执行编译后的代码。

Runtime System分为Legacy和Modern两个版本,一般来说,我们现在用的都是Modern版本。Modern版本的Runtime System有一个显著的特征就是“non-fragile”,即父类的成员变量的布局发生改变时,子类不需要重新编译。此外,还支持为声明的属性进行合成操作(即@property和@synthesis)。

下面会讨论NSObject类、Objective-C程序如何与Runtime System交互、运行时动态地加载类、发消息给其它对象,以及运行时如何获取对象信息。

与Runtime System交互

Objective-C程序和Runtime System在三个不同层次进行交互:通过Objective-C源码;通过NSObject定义的函数;以及通过直接调用runtime functions。

通常来讲,Runtime System都是在幕后工作,我们需要做的就是编写Objective-C代码,然后编译。编译器会为我们创建相应的数据结构和函数调用来实现语言的动态特性。这些数据结构保存着在类、Category定义和Protocol声明中所能找到的信息,包括成员变量模板、selectors,以及其它从源码中提取到的信息。

Runtime System是一个动态共享库,位于/usr/include/objc,拥有一套公共的接口,由一系列函数和数据结构组成。开发人员可以使用纯C调用一些函数来做编译器做的事情,或者扩展Runtime System,为开发环境制作一些工具等等。尽管一般情况下,编写Objective-C并不需要了解这些内容,但有时候会很有用。所有的函数都在Objective-C Runtime Reference有文档化信息。

Cocoa中大部分对象都是NSObject的子类(NSProxy是一个例外),继承了NSObject的方法。因此在这个继承体系中,子类可以根据需求重新实现NSObject定义的一些函数,实现多态和动态性,比如description方法(返回描述自身的字符串,类似Python中开头的三引号)。

一些NSObject定义的方法只是简单地询问Runtime System获得信息,使得对象可以进行自省(introspection),比如用来确定类类型的isKindOfClass:,确定对象在继承体系中的位置的isMemberOfClass:,判断一个对象是否能接收某个特定消息的respondsToSelector:,判断一个对象是否遵循某个协议的conformsToProtocol:,以及提供方法实现地址的methodForSelector:。这些方法让一个对象可以进行自省(introspect about itself)。

最主要的Runtime函数是用来发送消息的,它由源码中的消息表达式激发。发送消息是Objective-C程序中最经常出现的表达式,而该表达式最终会被转换成objc_msgSend函数调用。比如一个消息表达式[receiver message]会被转换成objc_msgSend(receiver, selector),如果有参数则为objc_msgSend(receiver, selector, arg1, arg2, …)。

消息只有到运行时才会和函数实现绑定起来:首先objc_msgSend在receiver中查找selector对应的函数实现;然后调用函数过程,将receiving object(即this指针)和参数传递过去;最后,返回函数的返回值。

发送消息的关键是编译器为类和对象创建的结构,包含两个主要元素,一个是指向superclass的指针,另一个是类的dispatch table,该dispatch table中的表项将selector和对应的函数入口地址关联起来。

当一个对象被创建时,内存布局中的第一个元素是指向类结构的指针,isa。通过isa指针,一个对象可以访问它的类结构,进而访问继承的类结构。示例图可参见此处。

当向一个对象发送消息时,objc_msgSend先通过isa指针在类的dispatch table中查找对应selector的函数入口地址,如果没有找到,则沿着class hierarchy(类的继承体系)寻找,直到NSObject类。这就是在运行时选择函数实现,用OOP的行话来说,就是动态绑定。

为了加速发送消息的速度,Runtime System为每个类创建了一个cache,用来缓存selector和对应函数入口地址的映射。

当objc_msgSend找到对应的函数实现时,它除了传递函数参数,还传递了两个隐藏参数:receiving object和selector。之所以称之为隐藏参数,是因为这两个参数在源代码中没有显示声明,但还是可以通过self和_cmd来访问。

当一个消息要被发送给某个对象很多次的时候,可以直接使用methodForSelector:来进行优化,比如下述代码:

//
void (*setter)(id, SEL, BOOL);
int i;  setter = (void (*)(id, SEL, BOOL))[targetmethodForSelector:@selector(setFilled:)];
for ( i = 0; i < 1000, i++ )setter(targetList[i], @selector(setFilled:), YES);
//

其中,methodForSelector:是由Cocoa Runtime System提供的,而不是Objective-C本身的语言特性。这里需要注意转换过程中函数类型的正确性,包括返回值和参数,而且这里的前两个参数需要显示声明为id和SEL。

[3] 方法的动态决议

有时候我们想要为一个方法动态地提供实现,比如Objective-C的@dynamic指示符,它告诉编译器与属性对应的方法是动态提供的。我们可以利用resolveInstanceMethod:和resolveClassMethod:分别为对象方法和类方法提供动态实现。

一个Objective-C方法本质上是一个拥有至少两个参数(self和_cmd)的C函数,我们可以利用class_addMethod向一个类添加一个方法。比如对于下面的函数:

//
void dynamicMethodIMP(id self, SEL _cmd) {// implementation ….
}
//

我们可以利用resolveInstanceMethod:将它添加成一个方法(比如叫resolveThisMethodDynamically):

//
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{if (aSEL == @selector(resolveThisMethodDynamically)) {class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");return YES;}return [super resolveInstanceMethod:aSEL];
}
@end
//

动态决议和发送消息并不冲突,在消息机制起作用之前,一个类是有机会动态决议一个方法的。当respondsToSelector:或者instancesRespondToSelector:被激活时,dynamic method resolver会优先有个机会为这个selector提供一份实现。如果实现了resolveInstanceMethod:,对于不想动态决议而想让其遵循消息转发机制的selectors,返回NO即可。

Objective-C程序可以在运行时链接新的类和category。动态加载可以用来做很多不同的事情,比如System Preferences里头各种模块就是动态加载的。尽管有运行时函数可以动态加载Objective-C模块(objc/objc-load.h中的objc_loadModules),但Cocoa的NSBundle类提供了更方便的动态加载接口。

[4] 消息转发

向一个对象发送它不处理的消息是一个错误,不过在报错之前,Runtime System给了接收对象第二次的机会来处理消息。在这种情况下,Runtime System会向对象发一个消息,forwardInvocation:,这个消息只携带一个NSInvocation对象作为参数——这个NSInvocation对象包装了原始消息和相应参数。

通过实现forwardInvocation:方法(继承于NSObject),可以给不响应的消息一个默认处理方式。正如方法名一样,通常的处理方式就是转发该消息给另一个对象:

//
- (void)forwardInvocation:(NSInvocation *)anInvocation
{if ([someOtherObject respondsToSelector:[anInvocation selector]])[anInvocation invokeWithTarget:someOtherObject];else[super forwardInvocation:anInvocation];
}
//

对于不识别的消息(在dispatch table中找不到),forwardInvocation:就像一个中转站,想继续投递或者停止不处理,都由开发人员决定。

[5] 类型编码

为了支持Runtime System,编译器将返回值类型、参数类型进行编码,相应的编译器指示符是@encode。

比如,void编码为v,char编码为c,对象编码为@,类编码为#,选择符编码为:,而符合类型则由基本类型组成,比如

typedef struct example {id     anObject;char *aString;int anInt;
} Example;

编码为{example=@*i}。

[6] 属性声明

当编译器遇到属性声明时,它会生成一些可描述的元数据(metadata),将其与相应的类、category和协议关联起来。存在一些函数可以通过名称在类或者协议中查找这些metadata,通过这些函数,我们可以获得编码后的属性类型(字符串),复制属性的attribute列表(C字符串数组)。因此,每个类和协议的属性列表我们都可以获得。

与类型编码类似,属性类型也有相应的编码方案,比如readonly编码为R,copy编码为C,retain编码为&等。

通过property_getAttributes函数可以后去编码后的字符串,该字符串以T开头,紧接@encode type和逗号,接着以V和变量名结尾。比如:


@property char charDefault;

描述为:Tc,VcharDefault

@property(retain)ididRetain;

描述为:T@,&,VidRetain

Property结构体定义了一个指向属性描述符的不透明句柄:typedef struct objc_property *Property;。

通过class_copyPropertyList和protocol_copyPropertyList函数可以获取相应的属性数组:

objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)

通过property_getName函数可以获取属性名称。

通过class_getProperty和protocol_getProperty可以相应地根据给定名称获取到属性引用:

objc_property_t class_getProperty(Class cls, const char *name)
objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)

通过property_getAttributes函数可以获取属性的@encode type string:

const char *property_getAttributes(objc_property_t property)

以上函数组合成一段示例代码:

需要引入库

libobjc.A.dylib    (库)

#import <objc/runtime.h>  (头)

@interface Lender : NSObject {float alone;
}
@property float alone;
@end  id LenderClass = objc_getClass("Lender");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
for (i = 0; i < outCount; i++) {objc_property_t property = properties[i];fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}

转载于:https://www.cnblogs.com/DamonTang/archive/2012/08/24/2654154.html

Objective-C的Runtime System相关推荐

  1. 刨根问底Objective-C Runtime(2)- Object Class Meta Class

    刨根问底Objective-C Runtime(2)- Object & Class & Meta Class Chun Tips 专注iOS开发 刨根问底Objective-C Ru ...

  2. Run-time system与虚拟机

    Run-time system与虚拟机 一个Run-time系统,由一个执行引擎(Execution Engine).一个机器级的调试器(Debugger).一个汇编器(Assembler)和与之配套 ...

  3. 刨根问底Objective-C Runtime

    该笔记分为四篇: 刨根问底Objective-C Runtime(1)- Self & Super 刨根问底Objective-C Runtime(2)- Object & Class ...

  4. 刨根问底Objective-C Runtime(1)- Self Super

    转:http://chun.tips/blog/2014/11/05/bao-gen-wen-di-objective%5Bnil%5Dc-runtime(1)%5Bnil%5D-self-and-s ...

  5. Objective C运行时(runtime)技术总结,好强大的runtime

    前言:          Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表.属性列表.变量列表,修改方法.属性,增加方法,属性等等,本文对相 ...

  6. 从 JIT 编译看 Runtime 的过去与未来

    作者简介 常开颜 中国科学院计算技术研究所直博生,研究方向为硬件编程语言.编译技术. 如果读者想了解更多有关 Runtime 相关的技术内容,欢迎加入编程语言社区 SIG-Runtime. 加入方式: ...

  7. RunTime技术总结

    前言:          Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表.属性列表.变量列表,修改方法.属性,增加方法,属性等等,本文对相 ...

  8. iOS开发-Runtime详解(简书)

    简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [receiver message]; // ...

  9. 图解Android - Zygote, System Server 启动分析

    Init 是所有Linux程序的起点,而Zygote于Android,正如它的英文意思,是所有java程序的'孵化池'(玩过星际虫族的兄弟都晓得的).用ps 输出可以看到 >adb shell ...

最新文章

  1. pandas 合并数据
  2. java udp 接受阻塞_Java UDP发送与接收
  3. ES6学习(五)—数组的扩展
  4. PHP Zend Studio9.0怎么把代码搞成和服务器端的同步(就是直接在服务器端修改)
  5. 自动驾驶是一门怎样的生意?盘点5家创业公司商业落地的3条逻辑
  6. 计算机博士复试英语自我介绍,博士生复试自我介绍的英文
  7. iOS【面试】2018年面试题集锦
  8. 易如意php,易如意网络验证系统1.1【开源】
  9. 科学计算与MATLAB语言之基础知识
  10. 电赛练习之旋转倒立摆
  11. 软件设计模式与体系结构 期末课后题
  12. 常见操作系统体系结构
  13. 3dmax三点照明法场景照明的方法和技巧
  14. 微商新传奇奢瑞小黑裙、两家小程序内测成员都来这了,SDCC2016微信开发专题议题揭晓...
  15. 太阳直射点纬度计算公式_高中地理:正午太阳高度公式的应用
  16. ubuntu下好用的录屏软件之Vokoscreen
  17. 【题解】PAT (Basic Level) Practice (中文)
  18. SEM推广及广告投放数据分析及可视化
  19. 分享160个ASP源码,总有一款适合您
  20. 创意人像海报故障艺术海报教程故障艺术海报怎么做

热门文章

  1. PAT1002. 写出这个数
  2. 《流畅的python》之 设计模式, 装饰器
  3. 课节5:图神经网络算法(二)
  4. SAP License:SAP工业4.0 创新智能物流解决方案
  5. SAP License:COPA凭证冲销
  6. SAP License:HP面试手记
  7. 赛锐信息:SAP进化论
  8. 大鱼风控笔记 1:量化风控体系的风险板块
  9. 电商Sass平台-商城运营后台原型-仓储管理-订单管理-店铺运营-采购管理-数据分析-交易分析-留存分析-客户管理-用户运营-围栏管理-商品管理-流量分析-电商erp后台管理-用户权限-销量分析
  10. bzoj4195 [Noi2015]程序自动分析