联想
消息转发有点类似一起听说过的一个故事<<上帝的拯救>>,一次发洪水,传教士笃信上帝会当面就他,多次错过人其他人对他的帮助.其实那些人就是上帝派来拯救他.可惜传教士不懂变通,只能去见上帝了.
话题开头:
某个对象/类 调用方法,但是确没有实现该方法,最后给四次挽救的机会,若错过了,就只能"去见上帝了"
总体流程图如下:
![](https://img.haomeiwen.com/i7304108/963f883a15876fe4.png)
测试代码如下
- 创建一个Person类,仅在头文件进行方法的声明:
#pragma mark - 测试消息转发
- (void)testForwardMethod:(NSString*)methed;
然后在ViewDidLoad进行测试
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Person *p = [[Person alloc] init];
// TODO: 测试消息转发1
[p testForwardMethod:@"abc"];
}
若此时运行,💥奔溃
![](https://img.haomeiwen.com/i7304108/1b1ba22f5348f8d7.png)
来进行救赎第一次救赎:
注意:
1.1. 明确方法:确定是类方法还是对象方法
1.2. 调用 +resolveClassMethod: 或者resolveInstanceMethed
1.3. 由于本测试是对象方法, 实现方法如下.注意不要只返回YES,没有进行方法处理,那么结果还是奔溃.
void myMethodIMP(id self, SEL _cmd, id paragram)
{
NSLog(@"第一次救赎成功,参数:%@",paragram);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// 参数1: 要添加的类 参数2: 选择子/选择器(方法名) 参数3:方法的实现(方法函数) 参数4: 方法类型
// v@:@ 说明: v->返回值类型; @->代表方法第一个参数:self, :->代表选择器/选择子 @->代表第一个参数
// 具体查看
class_addMethod(self, sel, (IMP)myMethodIMP, "v@:@");
return YES;
}
第一次救赎结果如下:
![](https://img.haomeiwen.com/i7304108/e27452844a960686.png)
来进行救赎第二次救赎:
注意:(让其他类来执行)
2.1. 调用 +resolveClassMethod: 或者+resolveInstanceMethed 注释掉或者返回NO
2.2 在-forwardingTargetForSelector:进行判是否转发给有现实这个方法的对象或者类.
- (id)forwardingTargetForSelector:(SEL)aSelector {
Helper *helper = [[Helper alloc] init];
if([helper respondsToSelector:aSelector]) {
return helper;
} else {
return nil;
}
}
2.3. 转发的类要实现-testForwardMethod:方法,若没有实现相关方法,还是奔溃
#pragma mark - 测试消息转发2(其它类实现了方法)
- (void)testForwardMethod:(NSString*)methed {
NSLog(@"上帝的第二次救赎成功:%s -- %@ --- %@",__func__, methed, self);
}
第二次救赎结果如下:
![](https://img.haomeiwen.com/i7304108/5be65204f90493f1.png)
来进行救赎第三次救赎:
注意:
3.1. 转发的类要实现-testForwardMethod:方法,注释掉,第二次救赎-forwardingTargetForSelector:返回nil 类型编码详见
3.2 实现 -methodSignatureForSelector: 通过正确的类型方法方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
3.3 只要实现forwardInvocation: 就不会奔溃了
![](https://img.haomeiwen.com/i7304108/5392d7cb5e8d5c60.png)
不过还要进行正确的处理,例如转接给otherHelper处理,或者自己添加方法.
3.4 在forwardInvocation: 添加代码如下
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// 将其转交给helper
OtherHelper *otherHelper = [[OtherHelper alloc] init];
if([otherHelper respondsToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:otherHelper];
} else {
[super forwardInvocation:anInvocation];
}
}
3.5 注意:NSInvocation 方法的调用,含义target,selector
若要获取参数,和设置参数的时候注意隐含的两个参数self, _cmd 索引为2开始,注意获取self, _cmd 通过索引,获取self值的时候可能奔溃??
获取参数之前地址
![](https://img.haomeiwen.com/i7304108/f4d0caf7de85d251.png)
获取参数之后的地址,还会返回去调用.原因未找到?
![](https://img.haomeiwen.com/i7304108/757bc4db4835eef1.png)
- (void)getArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
第三次救赎结果如下:
转发其他类来处理
![](https://img.haomeiwen.com/i7304108/39e7d469a2e5313e.png)
自己添加方法来处理
![](https://img.haomeiwen.com/i7304108/d63897c862d9143a.png)
来进行救赎第四次救赎:
事不过三,何况操过三次了.处理基本都已经结束了.
实现:-doesNotRecognizeSelector: 在此方法中抛出错误! 然后奔溃💥
![](https://img.haomeiwen.com/i7304108/6f8110cd5db462c8.png)
网友评论