Objective-C学习之Runtime
(以下内容均为本人的理解,不敢保证准确性,如有误请指出,谢谢!)
1.Runtime是什么?
Runtime是一套组件,Objective-C正因为有了Runtime这套组件,才使得它成为一门完全动态的语言。有了Runtime,我们可以动态决定调用哪个方法(消息传递机制),添加方法,交换方法,给类添加属性,还有字典转模型等。
2.id类型
id类型可以指向任何一个对象,编译器在编译的时候无法知道该对象属于哪个类,只能到了程序运行的时候才知道具体属于哪个类
3.SEL类型
SEL是selector在Objective-C中的表示类型。selector可以理解为区别方法的ID。
4.IMP
IMP是“implementation”的缩写,它是由编译器生成的一个函数指针。当你发起一个消息后,这个函数指针决定了最终执行哪段代码。
5.Method
Method代表类中的某个方法的类型。
6.Ivar
Ivar代表类中实例变量的类型
7.objc_property_t
objc_property_t是属性
8.Cache
objc_msgSend每调用一次方法后,就会把该方法缓存到cache列表中,下次的时候,就直接优先从cache列表中寻找,如果cache没有,才从methodLists中查找方法。
9.Category
这个就是我们平时所说的类别了,它可以动态的为已存在的类添加新的方法。
一、消息机制
①消息传递机制
在面向对象编程中,对象调用方法叫发送消息。在编译时,程序的源代码就会从对象发送消息转换成Runtime的objc_msgSend函数调用。
例如:[receiver oneMethod];转化成objc_msgSend(receiver,oneMethod);
函数objc_msgSend调用过程大概如下:
(1)检测这个selector是否要忽略。不忽略则下一步。
(2)检测target是否为nil对象,nil对象接受任何消息都会忽略。target不为nil对象则下一步
(3)检测对象里是否有实现方法能响应这个消息,如果没有则向父类查找,如果父类没有则继续向上一级查找,直到根类,如果根类都不能响应,则进入消息转发过程。
②消息转发机制
当对象无法正常处理消息时,则进入这个过程:
(1)调用resolveInstanceMethod:方法决定是否动态添加方法。如果返回NO则进入下一步。
(2)调用forwardingTargetForSelector:方法,看当前对象是否有内部对象可以响应消息。有则返回内部对象,否则进入下一步。
(3)这一步将选择子,参数,接收对象封装成一个NSInvocation对象,然后调用forwardInvocation:方法。这时forwardInvocation:方法的实现有两种:一种是修改接收对象,这种实现与第二步返回内部对象类似;另一种实现则是修改消息,例如追加一个参数或修改selector等。如果forwardInvocation:方法没有实现,则进入下一步。
(4)到了这一步则调用doesNotRecognizeSelector,如果没有实现这个方法,则会crash,整个消息传递和消息转发过程结束。
二、Runtime的使用
要使用Runtime,先包含头文件。如果是模拟器则#import <objc/objc-runtime.h>,如果是真机则
#import <objc/runtime.h>和#import <objc/message.h>,可如下编写:
#if TARGET_IPHONE_SIMULATOR
#import <objc/objc-runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
Runtime机制提供了很多有用的方法,部分如下:
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes); //动态创建一个类
void objc_disposeClassPair(Class cls); //销毁一个类
SEL sel_registerName(const char *str); //注册一个方法
void objc_registerClassPair(Class cls); //注册一个类
void object_setIvar(id obj, Ivar ivar, id value); //为对象中的实例变量赋值
BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types); //给类添加变量
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types); //给类添加方法
Ivar class_getInstanceVariable(Class cls, const char *name); //从类中获取一个实例变量
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) //获取类的所有属性
const char *property_getName(objc_property_t property) //通过objc_property_t获取属性名称
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) //获取类的所有实例变量(包含属性背后的实例变量)
const char *ivar_getName(Ivar v) //通过Ivar获取实例变量的名称
Method *class_copyMethodList(Class cls, unsigned int *outCount) //获取类的实例方法(不包括类方法和继承的实例方法)
SEL method_getName(Method m) //通过Method获取方法SEL(标识)
const char *sel_getName(SEL sel) //通过SEL获取方法具体名称
unsigned int method_getNumberOfArguments(Method m) //通过Method获取方法参数个数