作为iOS开发者,runtime特性是必须了解的重点加分项。这并不是说你可以说出消息机制,运行时消息重定向,或者利用runtime特性实现交换方法等,而是更应该深入了解其原理,"知其然且知其所以然"才能不断成长。 Object-C是面相运行时的语言(runtime oriented language),它将编译和链接时要执行的逻辑延迟到运行时来处理。相对静态语言,我们的代码更具灵活性,在苹果封闭链中这个特性也能帮助我们进行线上修复,例如业界鼎鼎大名的JSPatch(虽然现在苹果为了安全性,一封邮件“封杀”JSPatch)。那本篇文章先从runtime功能入手,让你体会runtime的强大,再介绍其原理。在此之前,先了解下基础知识。
传送门
- Objective-C Runtime Programming Guide苹果官方文档
Objective-C Runtime 是什么?
Object-C的runtime是一个运行时库(官方传送门:https://opensource.apple.com/tarballs/objc4/),是一套纯C(C和汇编)API,感兴趣的可以下载官方源码。Objective-C runtime 创建了所有需要的结构体,让 Objective-C 的面相对象编程变为可能。
术语
1、Objective-C runtime包括两个版本,Modern Runtime和 Legacy Runtime:
- Modern Runtime(现代的Runtime):64 位的 Mac OS X 应用和所有 iPhone OS 的应用
- Legacy Runtime: 所有 32 位的 Mac OS X 应用
2、Method 有 2 种基本类型的方法:
- Instance Method(实例方法):以 ‘-’ 开始,比如 -(void)doFoo; 在对象实例上操作
- Class Method(类方法):以 ‘ ’ 开始,比如 (id)alloc。方法(Methods)和 C 的函数很像,是一组代码,执行一个小的任务。如- (NSString *)movieTitle
3、Selector Selector 在 Objective-C 中 selector 只是一个 C 的数据结构,用于表示一个你想在一个对象上执行的 Objective-C 方法。
4、Meta Classes 类类型
基础重点
代码语言:javascript复制typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
这段代码 5、objc_class Objective-C 中 NSObject是大多数类的根类。
代码语言:javascript复制@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
从中可以看出NSObject有一个isa属性,类型是Class。Class 是一个 objc_class 结构类型的指针
代码语言:javascript复制struct objc_class {
struct objc_class *isa;
struct objc_class *super_class;
const char *name;
long version;
long info;
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
};
可以看到Objective-C对象系统的基石:struct objc_class结构如下:
- isa指针:指向该对象所属类型的类型对象(Class Object)。在Objective-C中,类也是用对象来表示的,而类的isa指针指向它的metaclass(存储静态成员变量和类方法)。
- super_class指针:指向父类。
- name:类名称。
- version:类的版本信息。
- info:运行期使用的标志位,比如0x1(CLS_CLASS)表示该类为普通class,0x2(CLS_META)表示该类为 metaclass。
- instance_size:实例大小,即内存所占空间。
- ivars:指向成员变量列表的指针。
- methodLists:根据标志位的不同可能指向不同,比如可能指向实例方法列表,或者指向类方法列表。
- cache:因为Objective-C的消息转发需要查找dispatch table甚至可能需要遍历继承体系,所以缓存最近使用的方法。
- protocols:类需要遵守的协议。
6、objc_object objc_object 只有一个指向类的 isa 指针,就是我们说的术语 “isa pointer”(isa 指针)。
7、isa 指针 isa 指针是当你向对象发送消息时,Objective-C Runtime 检查一个对象并且查看它的类是什么然后开始查看它是否响应这些 selectors 所需要的一切。
8、id指针 id数据类型是一个指向struct objc_object结构的指针,这个指针包含一个纸箱objc_class结构的isa 指针。 更确切地说,id是指向Class类型的指针,而Class又是指向struct objc_class结构的指针。
9、 Class Cache 就是class_object定义中的struct objc_cache *cache; 当 Objective-C runtime 沿着一个对象的 isa 指针检查时,它会发现一个对象实现了许多的方法。然而你可能只调用其中一小部分的方法,也没有意义每次检查时搜索这个类的分发表(dispatch table)中的所有 selector。所以这个类实现了一个缓存,当你搜索一个类的分发表,并找到合适的 selector 后,就会把它放进缓存中。所以当 objc_msgSend() 在一个类中查找 selector 时会先查找类缓存。有个理论是,当你在一个类上调用了一个消息,你很可能之后还会调用它。
runtime 常用开发应用场景
http://www.jianshu.com/p/19f280afcb24,“白开水”的这篇文章介绍的很好,标题“面试、工作”就赢了
- runtime 消息机制 消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现。 我们写 OC 代码,它在运行的时候也是转换成了 runtime 方式运行的。任何方法调用本质:就是发送一个消息(用 runtime发送消息,OC 底层实现通过 runtime 实现),每一个 OC 的方法,底层必然有一个与之对应的 runtime 方法。 OC的函数调用成为消息发送,属于 动态调用过程。在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
作用
利用runtime 可以做一些OC不容易实现的功能:
- 动态交换两个方法的实现(特别是交换系统自带的方法)
- 动态添加对象的成员变量和成员方法
- 获得某个类的所有成员方法、所有成员变量
面试题 1、method swizzling?
2、objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系? objc_msgSend()是[obj foo]的具体实现。 在runtime中,objc_msgSend()是一个c函数,[obj foo]会被翻译成这样的形式objc_msgSend(obj, foo)。 1、去obj的对应的类中找方法 2、先找缓存,找不到再去找方法列表, 3、再找父类,如此向上传递。 4、最后再找不到就要转发。
3、什么时候会报 Unrecognized selector 的异常? 当调用对象的某个方法的时候, 如果在当前类中没有找到此方法, 那么就到当前类的父类中去寻找, 如果在父类中没有找到, 那么就去父类的父类中去寻找, 一直找到 NSObject 都没有这个方法, 就会报 Unrecognized selector 的异常.
4、runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法) IMP寻址 http://blog.csdn.net/dp948080952/article/details/52437451
5、_objc_msgForward函数是做什么的,直接调用它将会发生什么? http://www.jianshu.com/p/04ba5f3bfc2b
6、消息机制方法调用流程 方法:实例方法,类方法 http://www.jianshu.com/p/d5ca2cdd4fa7 对官方文档的翻译,略显粗糙,适用于我这种英文比较烂的童鞋?