iOS开发收集

Runtime

2016-03-02  本文已影响44人  Maple_chang

[obj message]调用执行过程如下图:


1456815016258663.png

一个函数是由一个selector(SEL),和一个implement(IML)组成的。Selector相当于门牌号,而Implement才是真正的住户(函数实现)。
一个OC对象执行[obj message]方法后,如果message并没有被obj所属类实现,会报unrecognized selector sent to instance错误,runtime给了我们3次机会去避免发生这样的情况。
1.OC对象在收到无法解读的消息后,首先会调用所属类的+ (BOOL)resolveInstanceMethod:(SEL)sel,这个方法在运行时,没有找到SEL的IML时就会执行。这个函数是给实例对象利用class_addMethod添加函数的机会。根据文档,如果实现了添加函数代码则返回YES,未实现返回NO。resolveClassMethod:是用于动态解析一个类方法;而resolveInstanceMethod:是用于动态解析一个实例方法。

//类函数
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically))
    {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSel];
}```
关于class_addMethod这个方法,是这样定义的

OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
cls 在这个类中添加方法,也就是方法所添加的类
name 方法名,这个可以随便起的
imp 实现这个方法的函数
types 定义该数返回值类型和参数类型的字符串,这里比如"v@:",其中v就是void,带表返回类型就是空,@代表参数,这里指的是id(self),这里:指的是方法SEL(_cmd),比如再定义一个函数
int newMethod (id self, SEL _cmd, NSString *str) {

return 100;
}
那么添加这个函数的方法就应该是ass_addMethod([self class], @selector(newMethod), (IMP)newMethod, "i@:@");

2.如果在+ (BOOL)resolveInstanceMethod:(SEL)sel中没有找到或者添加方法,消息继续往下传递到`- (id)forwardingTargetForSelector:(SEL)aSelector`看看是不是有对象可以执行这个方法,该方法返回未被接收消息最先被转发到的对象。如果一个对象实现了这个方法,并返回一个非空的对象(且非对象本身),则这个被返回的对象成为消息的新接收者。另外如果在非根类里面实现这个方法,如果对于给定的selector,我们没有可用的对象可以返回,则应该调用父类的方法实现,并返回其结果。

//将消息转出某对象

3.当前面两步都无法处理消息时,运行时系统便会给接收者最后一个机会,将其转发给其它代理对象来处理。这主要是通过创建一个表示消息的`NSInvocation`对象并将这个对象当作参数传递给`forwardInvocation:`方法。我们在`forwardInvocation:`方法中可以选择将消息转发给其它对象。
在这个方法中,主要是需要做两件事:
(1).找到一个能处理anInvocation调用的对象。
(2).将消息以anInvocation的形式发送给对象。anInvocation将维护调用的结果,而运行时则会将这个结果返回给消息的原始发送者。

真正执行从 `methodSignatureForSelector:`返回的NSMethodSignature。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能以Selector的形式转向一个对象)

`- (void)doesNotRecognizeSelector:(SEL)aSelector`
作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。
虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。
#使用场景:
在一个函数找不到时,Objective-C提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
如果都不中,调用doesNotRecognizeSelector抛出异常。

参考:
http://www.cocoachina.com/ios/20160302/15494.html
http://www.cnblogs.com/biosli/p/NSObject_inherit_2.html
http://www.cocoachina.com/ios/20150205/11113.html
上一篇 下一篇

猜你喜欢

热点阅读