- iOS开发过程中,有一类的错误会经常遇到,就是找不到所调用的方法,当然这类问题比较好解决,给当前对象或其父类对象添加该方法即可,使得编译器在编译时能正确找到该方法;或者,还有另外的方法,由于Objective-C是一门动态语言,我们也可以在运行期再给类添加该方法,一样可以解决该问题,而这就涉及了类的消息转发机制。
上图为消息转发全部流程,分为三步,感觉是一种递进式,倒不是能做到的事情递进,是想让你做的事递进发展。比如第一步动态的添加一个方法,第二步重新选择个对象作为接收者,如果第一步第二步你都不选择,那么你可能需要一个更全面的转发方式。
Person *person = [Person new];
// 调用Person类对象的needDo方法
[person performSelector:@selector(needDo)];
如果Person类不实现needDo 方法就会报错。
2019-03-14 16:36:46.833003+0800 Test[4424:39573] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person needDo]: unrecognized selector sent to instance 0x600001bf8a00'
这样就可以用到消息转发化解掉这个报错。
第一步
动态添加方法
可以通过重写以下两个方法
// 处理类方法转发
+ (BOOL)resolveClassMethod:(SEL)sel{
return NO;
}
// 处理实例方法转发
+ (BOOL)resolveInstanceMethod:(SEL)sel{
return NO;
}
具体做法:
+ (BOOL)resolveInstanceMethod:(SEL)sel{
NSString *selStr = NSStringFromSelector(sel);
if ([selStr isEqualToString:@"needDo"]){
class_addMethod(self, sel, (IMP)iDothis, "v@:");
//这里YES和NO效果一样 我也不知道为啥
return YES;
}
return NO;
}
void iDothis(){
printf("第一步转发 do");
}
第二步
重新选择接受对象
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSString *selStr = NSStringFromSelector(aSelector);
if ([selStr isEqualToString:@"needDo"]){
// PersonSon 类实现needDo方法
return [PersonSon new];
}
return nil;
}
第三步
最后一步 完整的消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *selStr = NSStringFromSelector(aSelector);
if ([selStr isEqualToString:@"needDo"]){
return [NSMethodSignature signatureWithObjCTypes:"v@:@@"];
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSString *sleep = @"sleep";
NSString *work = @"work";
[anInvocation setTarget:self];
[anInvocation setArgument:&sleep atIndex:2];
[anInvocation setArgument:&work atIndex:3];
[anInvocation setSelector:@selector(toSleep:or:)];
[anInvocation invoke];
}
- (void)toSleep:(NSString*)sleep or:(NSString*)work{
NSLog(@"just %@ or %@",sleep,work);
}
网友评论