在网上看了好多小伙伴关于消息转发的博客、贴子,感觉是runtime中一种非常常用的技术,刚开始还不懂它有什么用处,但是敲了部分相关代码后,感觉它应该解决的是当我们创建一个对象并且来调用它的某个方法的时候,如果这个方法只有声明,但是没有实现的话,就会导致程序Crash掉,为了防止这种情况的发生,我们用runtime的消息转发机制来防止程序的Crash。


另外,我们在做对多个对象处理的时候,可以考虑通过多个对象调用某一个类里面的方法来实现消息转发,那么这也会让我们统一化,类似工场方法那样,当我们想调用任何一个子类的方法时,都可以先走父类的方法,在消息转发中,我们可以通过多个对象调用某一个类的方法来实现类似的效果,真的很神奇有木有


好啦,废话不多说,无代码无权威,上代码


首先我们创建一个类,声明一个方法,但是不实现它


 
@interface SWHModel : NSObject- (void)beginUp;@end


接下来我们在Controller里面来调用它,看看是什么效果

没错和预期的一样,程序Crash掉了,因为对象没有找到对应的方法


 
- (void)buildData {SWHModel *model = [[SWHModel alloc] init];[model beginUp];
}


OK,我们调用消息转发的第一种方法


 
void begin (id self, SEL _cmd) {NSLog(@"%@ %s", self, sel_getName(_cmd));
}+ (BOOL)resolveInstanceMethod:(SEL)sel {if (@selector(beginUp) == sel) {class_addMethod(self, sel, (IMP)begin, "v@:");return YES;}return [super resolveInstanceMethod:sel];
}


这里的class_addMethod方法为当前类动态添加一个指示方法,而当前函数的第三个参数为添加一个C中的函数,所以我们在这里创建一个C函数,另外"v@:"表示的是void self Selector

你会发现它直接调到C函数中了哦


然后是第二种方法


 
- (id)forwardingTargetForSelector:(SEL)aSelector {return [[SWHNextModel alloc] init];
}


没错,我在SWHNextModel类中声明并且实现了这个方法,然后我们利用消息转发,直接将当前对象转发到新创的这个类对象,这样就会直接调用这个类对象里面的方法了哦。


最后是第三种方法

也是最安全的方法,我一般会直接用第三种方法


 
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {NSString *sel = NSStringFromSelector(aSelector);if ([sel isEqual:@"beginUp"]) {return [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
}- (void)forwardInvocation:(NSInvocation *)anInvocation {SEL selector = [anInvocation selector];SWHNextModel *model = [[SWHNextModel alloc] init];if ([model respondsToSelector:selector]) {[anInvocation invokeWithTarget:model];}
}


其实第三个方法算是前两者的汇总,首先该对象会走获取NSMethodSignature来获取方法标签,这个时候我们为它加个判断,然后给它一个需要的type,接下来在Invocation方法里面创建一个新的对象,并且将其唤醒,这个时候我们就会直接调用新对象里面的方法了哦


哈,没错,这三种方法都可以解决上述出现的问题,也就是都可以实现消息转发


确实蛮有意思的呢



回头再调研一下其它的方法咯