接着前面的说,之前说了runtime的交互、runtime的消息机制、传递、方法地址获取。
四、Dynamic Method Resolution
动态方法解析,就是runtime提供了给类动态添加方法的实现。比如类的属性,会自动添加setter和getter方法就是如此,在编译时,将动态的添加与该属性相关联的setter和getter方法。
可以通过实现这些方法resolveInstanceMethod:
,resolveClassMethod:
,并分别为实例和类方法动态提供给定选择器的实现。
/// C函数,本身就是一个IMP指针
void dynamicMethodIMP(id self,SEL _cmd){
//实施....
}
@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
我们看到,动态添加方法使用C函数:
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types);
第一个参数是类,即当前对象的类。
第二个是一个方法选择器,我理解就是一个方法名。
第三个是一个指向实现的函数指针,如果是C函数直接强转即可,如果是OC方法,则需要获取。可以使用以下方法获取:
IMP imp = class_getMethodImplementation([receiver class], @selector(message));
或者
Method m = class_getInstanceMethod([receiver class], @selector(message));
IMP imp = method_getImplementation(m);
第四个是方法的编码,一组描述方法、返回值及参数类型特征的字符。
比如加入的方法是int sum(id self, SEL _cmd, int a, int b)
,那么这里是:class_addMethod([receiver class], @selector(sum), (IMP) sum, "i@:ii");
,i
表示int(An int,返回值类型),@
表示self(An object,Object类型),:
表示_cmd(A method selector (SEL),方法选择器类型),ii
分别表示后面两个参数(An int,int类型)。这里苹果有提供一个类型对照表:
通常消息转发和动态方法解析是互不相干的。在进入消息转发机制之前, respondsToSelector:和instancesRespondToSelector: 会被首先调用。您可以在这两个 方法中为传进来的选标提供一个IMP。如果您实现了resolveInstanceMethod:方法但是仍然希望正 常的消息转发机制进行,您只需要返回NO就可以了。
Dynamic Loading
动态加载可以用在很多地方。例如,系统配置中的模块就是被动态加载的。
在 Cocoa 环境中,动态加载一般被用来对应用程序进行定制。您的程序可以在运行时加载其他程序员编写 的模块——和 Interface Build 载入定制的调色板以及系统配置程序载入定制的模块的类似。 这些模块通过 您许可的方式扩展了您的程序,而您无需自己来定义或者实现。您提供了框架,而其它的程序员提供了实 现。
尽管已经有一个运行时系统的函数来动态加载Mach-O文件中的Objective-C模块 (objc_loadModules,在objc/objc-load.h中定义),Cocoa的NSBundle类为动态加载 提供了一个更方便的接口——一个面向对象的,已和相关服务集成的接口。关于NSBundle类的更多相关
信息请参考Foundation框架中关于NSBundle类的文档。关于Mach-O文件的有关信息请参考Mac OS X ABI Mach-O 文件格式参考库。
五、Message Forwarding
通常,给一个对象发送它不能处理的消息会得到出错提示,然而,Objective-C 运行时系统在抛出错误之前, 会给消息接收对象发送一条特别的消息来通知该对象。这就是消息转发。
如果一个对象收到一条无法处理的消息,运行时系统会在抛出错误前,给该对象发送一条
forwardInvocation:消息,该消息的唯一参数是个 NSInvocation 类型的对象——该对象封装了 原始的消息和消息的参数。
您可以实现 forwardInvocation:方法来对不能处理的消息做一些默认的处理,也可以以其它的某种 方式来避免错误被抛出。如 forwardInvocation:的名字所示,它通常用来将消息转发给其它的对象。