1.Objective-C中方法的调用在Runtime期间,都会转为objc_msgSend(receiver, selector, arg1, arg2, ...).默认的objc_msgSend会接收两个隐藏的参数,一个是消息接收者(一般默认为self),一个是方法(默认为_cmd).
当objc_msgSend找不到方法的时候就会进入消息转发的流程
1resolveInstanceMethod 或 resolveClassMethod方法是消息转发的第一步,当这个函数返回YES时代表找到了方法就不会继续转发,当这个函数返回NO时,会继续转发.(备注:respondsToSelector和instancesRepondToSelector两个方法就是通过这个函数去检测的)
2.在1resolveInstanceMethod内部若是没有处理这个情况,那么消息会进一步转发到(id)forwardingTargetForSelector:(SEL)aSelector这个函数,这个函数返回的是消息的接收者,你可以选择在这个方法内,将消息转发到另一个对象上.
3.若是forwardingTargetForSelector没有做任何处理,消息会到达(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector这个函数,这个函数返回的是函数的签名,在这个方法内部选择返回函数签名,若签名为nil则消息直接到达doesNotRecognizeSelector这个方法,抛出错误.
4.若是在methodSignatureForSelector方法内部返回返回函数签名,则消息到达(void)forwardInvocation:(NSInvocation *)anInvocation这个函数.这个函数中anInvocation参数包含了函数签名,参数,target.SEL等信息.
这是最后一步的转发,若是不处理消息,则消息会到达doesNotRecognizeSelector这个函数,然后抛出错误.
备注:
1.获取函数签名可以使用[NSMethodSignature signatureWithObjCTypes:"@@:@I"]方法获取签名,方法的参数是函数的符号表达式(个人理解),其中"@:"表示Selector ,"@:"前面一位表示的是函数的返回值,后面的表示函数的参数.例如
-(id)dynaFunc:(NSString *)name :(int)age;
其转换成字符串的表达式为"@@:@i".具体的Type Encoding请参考Type Encoding.
不过这种编码方式很复杂,可以使用[_dy methodSignatureForSelector:aSelector]方法获取参数,调用该方法的对象是你要转发的对象,同时记得这个对象必须和forwardInvocation中Invocation的Target是同一个对象.
2.forwardInvocation 该方法中Invocation可以设置其Target,Arguments,记住设置参数时,从索引2开始,记得前面说的,Objective-C中方法有两个隐藏的参数 self ,_cmd 吗,这就是为什么设置参数从索引2开始.