Runtime消息动态解析与转发流程
程序员文章站
2022-03-14 10:09:05
先上图: 下面根据具体代码看这张图。 一、创建一个Person类, Person.h Person.m 大家可以看到,Person类只声明了 sendMessage:方法,在.m文件里没有实现这个方法。 这时,如果在viewController中调用Person类的sendMessage方法,程序会 ......
先上图:
下面根据具体代码看这张图。
一、创建一个Person类,
Person.h
#import <Foundation/Foundation.h> @interface Person : NSObject -(void)sendMessage:(NSString *)message; @end
Person.m
#import "Person.h" #import <objc/runtime.h> @implementation Person @end
大家可以看到,Person类只声明了 sendMessage:方法,在.m文件里没有实现这个方法。
这时,如果在viewController中调用Person类的sendMessage方法,程序会发生崩溃。
#import "ViewController.h" #import "Person.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[[Person alloc]init] sendMessage:@"Hello"]; }
结合上面的图片,我们说说消息处理的机制。
1.当我们调用的方法没有具体的实现时,会调用
+ (BOOL)resolveInstanceMethod:(SEL)sel;
+(BOOL)resolveInstanceMethod:(SEL)sel{ NSString *methodName = NSStringFromSelector(sel); if ([methodName isEqualToString:@"sendMessage:"]) { //我们可以在这里添加方法的实现 return class_addMethod(self, sel, (IMP)sendMessage, "v@:@"); } return NO; } void sendMessage (id self, SEL _cmd, NSString *message){ NSLog(@"message=%@",message); }
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types):为类动态添加方法。如果有同名会返回NO,成功返回YES。
其中的参数types查询地址:)
2. 如果 resolveInstanceMethod:方法返回NO,调用
-(id)forwardingTargetForSelector:(SEL)aSelector;
这个方法是找备用者,比如:Animal类。
Animal.h
#import <Foundation/Foundation.h> @interface Animal : NSObject @end
Animal.m
#import "Animal.h" @implementation Animal -(void)sendMessage:(NSString *)message{ NSLog(@"message=%@",message); } @end
Animal类没有声明sendMessage:方法,但在.m文件里有这个方法的实现,可以作为备用者。如下:
-(id)forwardingTargetForSelector:(SEL)aSelector{ NSString *methodName = NSStringFromSelector(aSelector); if ([methodName isEqualToString:@"sendMessage:"]) { if ([[Animal new] respondsToSelector:aSelector]) { return [Animal new]; } } return [super forwardingTargetForSelector:aSelector]; }
3. 如果 forwardingTargetForSelector:(SEL)aSelector返回 nil。
// 若前两种方法都不处理,则走这里 // 1)方法签名 -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ NSString *methodName = NSStringFromSelector(aSelector); if ([methodName isEqualToString:@"sendMessage:"]) { return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; } return [super methodSignatureForSelector:aSelector]; } // 2) 签名后,消息转发,找备用者 -(void)forwardInvocation:(NSInvocation *)anInvocation{ SEL selector = [anInvocation selector]; Animal *animal = [Animal new]; if ([animal respondsToSelector:selector]) { [anInvocation invokeWithTarget:animal]; } else{ [super forwardInvocation:anInvocation]; } }
4.如果走到第3步,仍然不做处理,如下:
-(void)forwardInvocation:(NSInvocation *)anInvocation{ [super forwardInvocation:anInvocation]; }
这时为了程序的健壮性,防止崩溃,可以用以下方法处理。
// 若前3方法都不处理,为了防止崩溃,可调用此方法 -(void)doesNotRecognizeSelector:(SEL)aSelector{ NSString *methodName = NSStringFromSelector(aSelector); NSLog(@"找不到 %@ 这个方法的实现",methodName); }