Objective-C实例(类)对象调用一个方法,会首先在本类方法列表查找,如果没有,会在父类查找,直到根类NSObject,在任何一层找到方法就会立即执行,如果到了最后根类NSObject还没有找到,才会触发Objective-C Runtime的消息转发机制,最后触发 doesNotRecognizeSelector:
.
拦截异常有三次机会:
- +(BOOL)resolveInstanceMethod:(SEL)sel { }
如果当前对象调用了一个不存在的方法,Runtime会调用resolveInstanceMethod:
来进行动态方法解析, 我们需要用class_addMethod
函数完成向特定类添加特定方法实现的操作,返回false
,完成方法替换,结束消息转发,返回true
,则进入下一步forwardingTargetForSelector:
重定向.
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"1: 方法不存在,进入2");
NSString *selector = NSStringFromSelector(sel);
Class class = [self class];
if ([selector isEqualToString:@"foo"]) {
class_addMethod(class, sel, (IMP)fooMethod, "v@:@");
return false;
}
return true;
}
- -(id)forwardingTargetForSelector:(SEL)aSelector { }
如果步骤1
没有实现拦截,返回true
表示继续消息转发,在消息转发机制执行前,Runtime 系统会再给我们一次重定向的机会,通过重载forwardingTargetForSelector:
方法来替换消息的接受者为其他对象,返回有值,结束转发。返回nil
,则进步下一步forwardInvocation:
.
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"2: 方法不存在,进入3:");
NSString *selector = NSStringFromSelector(aSelector);
Class class = [self class];
if ([selector isEqualToString:@"other"]) {
class_addMethod(class, aSelector, (IMP)otherMethod, "v@:@");
return [BLSon new];
}
return [super forwardingTargetForSelector:aSelector];
}
- 如果
步骤1
、步骤2
都没有完成消息转发,则还有最后一条保命大法。
- 如果
//获取方法签名进入下一步,进行消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { }
//进行消息转发
- (void)forwardInvocation:(NSInvocation *)anInvocation { }
通常forwardInvocation:
,会用一个已知类,并且能响应异常方法的对象进行拦截。
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
BLFather *father = [BLFather new];
NSLog(@"4: 方法不存在,进入5:");
if ([father respondsToSelector:sel]) {
[anInvocation invokeWithTarget:father];
}
}
完整代码:
#import "BLSon.h"
#import <objc/runtime.h>
@implementation BLSon
/*
如果当前对象调用了一个不存在的方法
Runtime会调用resolveInstanceMethod:来进行动态方法解析
我们需要用class_addMethod函数完成向特定类添加特定方法实现的操作
返回NO,则进入下一步forwardingTargetForSelector:
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"1: 方法不存在,进入2");
NSString *selector = NSStringFromSelector(sel);
Class class = [self class];
if ([selector isEqualToString:@"foo"]) {
class_addMethod(class, sel, (IMP)fooMethod, "v@:@");
return false;
}
return true;
}
void fooMethod () {
NSLog(@"这是个新方法");
}
void otherMethod () {
NSLog(@"这是个other方法");
}
/*
在消息转发机制执行前,Runtime 系统会再给我们一次重定向的机会
通过重载forwardingTargetForSelector:方法来替换消息的接受者为其他对象
返回nil则进步下一步forwardInvocation:
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"2: 方法不存在,进入3:");
NSString *selector = NSStringFromSelector(aSelector);
Class class = [self class];
if ([selector isEqualToString:@"other"]) {
class_addMethod(class, aSelector, (IMP)otherMethod, "v@:@");
return [BLSon new];
}
return [super forwardingTargetForSelector:aSelector];
}
/*
获取方法签名进入下一步,进行消息转发
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSLog(@"3: 方法不存在,进入4:");
NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];
if (!methodSignature) {
methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];
}
return methodSignature;
}
/*
消息转发
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
BLFather *father = [BLFather new];
NSLog(@"4: 方法不存在,进入5:");
if ([father respondsToSelector:sel])
{
[anInvocation invokeWithTarget:father];
}
// 这里可以添加一些操作,用于定位异常,或自定义替换方法
// else {
// [anInvocation doesNotRecognizeSelector:sel];
// }
}
@end