一. forwardingTargetForSelector 快速转发
首先定义 YJPerson 类和 YJProxy 类,然后在 main 函数中调用say1方法。YJPerson 没有实现 say1 方法,YJProxy 类实现say1方法
@implementation LWPerson
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"--%s", __func__);
if (aSelector == @selector(say1)) {
return [[YJProxy alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
@end
@implementation YJProxy
- (void)say1 {
NSLog(@"--%s", __func__);
}
@end
调用:
int main(int argc, const char * argv[]) {
@autoreleasepool {
YJPerson *person = [[YJPerson alloc] init];
[person say1];
}
return 0;
}
输出:
打印结果显示:YJProxy 类和 YJPerson 类没有任何关系,但是指定给YJProxy 类,仍然最后可以查询到,并且没有崩溃消息,其实消息在查询过程中先去跟它 关系近的类中去查找,最后没找到。于是系统把这个权限丢给开发者,问你这事儿能不能你交给其它谁帮你整
二. forwardInvocation 慢速转发
慢速转发 forwardInvocation 也是消息查找的最后一个流程
大概意思就是说:除了实现 forwardInvocation:外, 还必须实现methodSignatureForSelector:。 转发消息的机制通过从methodSignatureForSelector: 返回的信息来创建要转发的 NSInvocation 对象。使用forwardInvocation,必须先给它生成个方法签名
代码测试:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"--%s", __func__);
NSMethodSignature *sign = [super methodSignatureForSelector:aSelector];
NSLog(@"sign = %@", sign);
return sign;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"--%s", __func__);
NSLog(@"%@ - %@", anInvocation.target, NSStringFromSelector(anInvocation.selector));
}
输出结果:
[super methodSignatureForSelector:aSelector]; 返回了 nil,结果直接奔溃了,正应了文档中说的 methodSignatureForSelector: 必须返回后一个 NSMethodSignature 对象。修改代码再来:
@interface YJPerson ()
@property (nonatomic, strong) YJProxy *helper;
@end
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"--%s", __func__);
if (aSelector == @selector(say1)) {
return [self.helper methodSignatureForSelector:@selector(say1)];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"--%s", __func__);
NSLog(@"%@ - %@", anInvocation.target, NSStringFromSelector(anInvocation.selector));
}
输出:
修改 forwardInvocation 实现:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"--%s", __func__);
NSLog(@"%@ - %@", anInvocation.target, NSStringFromSelector(anInvocation.selector));
if ([self.helper respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:self.helper];
}
}
输出:
这次指定了 target, 将原来 YJPerson对象 改为了 YJProxy;并成功调用了 -[YJProxy say1]。感觉这和快速转发 forwardingTargetForSelector 差不多,都是重新指定个对象,去实现。是这样么?再次修改 forwardInvocation 实现:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"--%s", __func__);
NSLog(@"%@ - %@", anInvocation.target, NSStringFromSelector(anInvocation.selector));
nInvocation.selector = @selector(say2);
anInvocation.target = self.helper;
[anInvocation invoke];
}
输出:
nInvocation 不仅能指定 target 还能指定 selector, 这就灵活多了,
总结:
-
快速转发:通过
forwardingTargetForSelector实现,如果此时有指定的对象去接收这个消息,就会走之指定对象的查找流程,如果返回是nil,进入慢速转发流程 -
慢速转发:通过
methodSignatureForSelector和forwardInvocation共同实现,如果methodSignatureForSelector返回值是nil,慢速查找流程结束-奔溃,如果有返回值forwardInvocation会执行,且可处理可不处理,都不会崩溃