Objective-C 调用方法实际上是发送消息的过程,先判断接收者是否为空,如果不为空经历以下几步:
1、先从方法的缓存中查找,如果没找到进入下一步。
2、通过对象的isa指针找到类对象,在类对象的方法列表中查找;如果没找到就通过superclass指针逐层查找直到NSObject对象。如果还是没找到就执行方法动态解析。
3、方法动态解析调用:+ (BOOL)resolveInstanceMethod:(SEL)sel
方法来为实例方法sel
动态添加实现方法。如果没添加进入下一步。
4、重定向消息的接收者,通过 - (id)forwardingTargetForSelector:(SEL)aSelector
来返回可处理这条消息的对象。如果这个对象不响应这个消息则进入消息转发。
5、消息转发调用 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
和 - (void)forwardInvocation:(NSInvocation *)anInvocation
如果通过以上过程还没有处理这条消息就会发生crash(unrecognized selector sent to instance xxxx
),
接下来一步步实现方法动态解析、重定向消息的接收者、消息转发
方法动态解析
首先我们发送一个未定义的消息:[self performSelector:@selector(message)];
让它进入方法解析,在方法解析中添加动态方法时需要导入#import <objc/runtime.h>
- (void)viewDidLoad {
[super viewDidLoad];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[self performSelector:@selector(message)];
#pragma clang diagnostic pop
}
void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@"%@ selector:%@", self, NSStringFromSelector(_cmd));
UIViewController *vc = (UIViewController *)self;
vc.view.backgroundColor = [UIColor lightGrayColor];
}
// 方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *crashStr = NSStringFromSelector(sel);
if ([crashStr isEqualToString:@"message"]) {
class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
结果:
Demo[6280:817719] <ViewController: 0x7f9298c03ae0> selector:message
重定向消息的接收者
1、创建一个Person类,并在.m文件中定义一个work
方法
# Person.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@end
NS_ASSUME_NONNULL_END
# Person.m
#import "Person.h"
@implementation Person
- (void)work {
NSLog(@"%@ work", self);
}
@end
2、我们将发送的消息 message
改为 work
,动态方法解析失败就调用 - (id)forwardingTargetForSelector:(SEL)aSelector
来为方法分配合适的接收者。
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[self performSelector:@selector(work)];
#pragma clang diagnostic pop
// 重定向消息接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
Person *person = [[Person alloc] init];
if ([person respondsToSelector:aSelector]) {// 判断接收者是否响应该方法
return person;
}
return nil;
}
结果:
Demo[6309:821439] <Person: 0x60000131bbd0> work
消息转发
我们再将发送的消息 work
改为 sendOtherMessage
,通过以上两步都没有能处理这条消息的方法就走消息转发流程。
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
[self performSelector:@selector(sendOtherMessage)];
#pragma clang diagnostic pop
// 消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
[anInvocation setSelector:@selector(forwardMethod)];
[anInvocation invokeWithTarget:self];
}
- (void)forwardMethod {
NSLog(@"%s", __func__);
}
结果:
Demo[6325:825256] -[ViewController forwardMethod]
来源:CSDN
作者:TengFengLian
链接:https://blog.csdn.net/u013592067/article/details/104678635