首先看一张图
![](https://img.haomeiwen.com/i1642566/c4389a0a0fe7c108.png)
当我们向目标对象发送的消息如[A B]向A对象发送B消息,而无法应答时,一般会报错“unrecognized selector sent to instance..”,而在报错前一般会经过如下几个方法:
+(BOOL)resolveInstanceMethod:(SEL)sel;
-(id)forwardingTargetForSelector:(SEL)aSelector;
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;
这意味着我们在报错前,有机会将错误的消息处理掉而达到我们想要的效果。
一、resolveInstanceMethod
对象无法处理testMyObject消息时会首先调用这个方法,这里我们在ViewController中重写这个方法:
+(BOOL)resolveInstanceMethod:(SEL)sel
{
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"testMyObject"]) {
class_addMethod([self class], sel, (IMP)myMethod,"v@:");
return YES;
}
//class_addmethod
//参数1:给谁添加
//参数2:添加的selector
//参数3:添加的imp实现
//参数4:"v@:"方法的签名,代表没有参数的方法。
return [super resolveInstanceMethod:sel];
}
void myMethod(id self, SEL _cmd) {
NSLog(@"我被调用了");
}
然后我们测试下:
-(void)testMethodRepost
{
NSLog(@"----------测试消息转发----------");
[self performSelector:@selector(testMyObject)];
}
输出结果:
**2016-03-03 10:52:57.233 FL_RUNTIME_DEMO[12084:4359110] ----------****测试消息转发****----------**
**2016-03-03 10:52:59.928 FL_RUNTIME_DEMO[12084:4359110] ****我被调用了**
在resolveInstanceMethod
中,我们判断发送的消息是不是“testMyObject”,如果是的,通过class_addMethod
向当前对象添加了一个新的方法testMyObject
,其中的IMP使用的是void myMethod(id self, SEL _cmd)
的实现。
二、forwardingTargetForSelector
在第一步的resolveInstanceMethod
中如果我们仍然无法处理问题,则会进入第二步。
我们删除之前的resolveInstanceMethod
后,重写这个forwardingTargetForSelector
方法:
-(id)forwardingTargetForSelector:(SEL)aSelector
{
NSString *selectorName = NSStringFromSelector(aSelector);
if ([selectorName isEqualToString:@"testMyObject"]) {
myObject *myobject = [[myObject alloc] init];
return myobject;
}
return [super forwardingTargetForSelector:aSelector];
}
其中myObject的实现是:
@interface myObject : NSObject
-(void)testMyObject;
@end
@implementation myObject
-(void)testMyObject
{
NSLog(@"测试成功");
}
@end
运行结果:
**2016-03-03 11:02:54.230 FL_RUNTIME_DEMO[12145:4367206] ----------****测试消息转发****----------**
**2016-03-03 11:02:56.950 FL_RUNTIME_DEMO[12145:4367206] ****测试成功**
在forwardingTargetForSelector
中,我们判断这条消息是否是“testMyObject”,如果是,我们返回了一个myObject实例。
这里实际上是对消息指定了一个新的发送对象,将"testMyObject"转发给了这个myObject实例由它来相应这个消息。
这里的返回值就是需要相应这个消息的对象。
由此我们实现了对消息的转发。
三、forwardInvocation
如果在上述两步中我们仍然无法对消息进行处理,则会进入forwardInvocation
这个方法中,不过在执行这个方法前会首先调用methodSignatureForSelector来请求一个签名,从而生成一个NSInvocation,对消息进行完全转发。
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (!signature) {
if([myObject instancesRespondToSelector:aSelector])
{
signature = [myObject instanceMethodSignatureForSelector:aSelector];
}
}
return signature;
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([myObject instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:[[myObject alloc] init]];
}
}
运行结果如下:
**2016-03-03 11:09:50.281 FL_RUNTIME_DEMO[12187:4373641] ----------****测试消息转发****----------**
**2016-03-03 11:09:54.460 FL_RUNTIME_DEMO[12187:4373641] ****测试成功**
网友评论