动态方法解析:
对象在收到无法解读的消息后,调用类方法+ (BOOL)resolveInstanceMethod:(SEL)sel来动态为其新增实例方法以处理该选择子。(如果尚未实现的方法是类方法,则调用+ (BOOL)resolveClassMethod:(SEL)sel)
新建HTResolveMethod类
①HTResolveMethod.h
#import@interfaceHTResolveMethod:NSObject@property(nonatomic, copy)NSString*name;@end
②HTResolveMethod.m
#import"HTResolveMethod.h"#import@implementationHTResolveMethod@dynamicname;/**
* 第一步:动态方法解析,征询接收者,看其是否能动态添加方法,来处理当前这个未知的选择子
* 为name动态添加set和get方法
*/+ (BOOL)resolveInstanceMethod:(SEL)sel { NSString *selectorStr = NSStringFromSelector(sel);/**
* i(类型为int)
* v(类型为void)
* @(类型为id)
* :(类型为SEL)
*/if([selectorStr isEqualToString:@"setName:"]) { class_addMethod(self, sel, (IMP)autoDictionarySetter,"v@:@"); }elseif([selectorStr isEqualToString:@"name"]) { class_addMethod(self, sel, (IMP)autoDictionaryGetter,"@@:"); }return[superresolveInstanceMethod:sel];}voidautoDictionarySetter(id self,SEL _cmd,id value) { NSLog(@"name的set方法==%@",value);}id autoDictionaryGetter(id self,SEL _cmd) {return@"name的get方法";}@end
③在ViewController.m中调用
HTResolveMethod *resolveMethod = [[HTResolveMethod alloc] init];resolveMethod.crashDelegate= self;resolveMethod.name= @"颖宝";NSLog(@"%@",resolveMethod.name);
重定向(备援接收者)
当不能在+ (BOOL)resolveInstanceMethod:(SEL)sel中动态添加方法处理选择子时,当前接收者还有一次机会处理未知的选择子。可以在- (id)forwardingTargetForSelector:(SEL)aSelector中把这条消息转给其他接收者来处理。
新建HTResolveMethod类
①HTResolveMethod.h
#import@interfaceHTResolveMethod:NSObject- (void)setupDatasWithTitle:(NSString*)title;@end
②HTResolveMethod.m
#import"HTResolveMethod.h"#import"HTForwardingTarget.h"@implementationHTResolveMethod/**
* 第二歩:进入消息转发流程重定向
* 将setupDatasWithTitle:转发到HTForwardingTarget类
*/- (id)forwardingTargetForSelector:(SEL)aSelector { NSString *selectorStr = NSStringFromSelector(aSelector);if([selectorStr isEqualToString:@"setupDatasWithTitle:"]) { HTForwardingTarget *forwardingTarget = [HTForwardingTargetnew];returnforwardingTarget; }return[superforwardingTargetForSelector:aSelector];}@end
新建HTForwardingTarget类
③HTForwardingTarget.m
-(void)setupDatasWithTitle:(NSString*)title { NSLog(@"重定向成功了,%@",title);}
④在ViewController.m中调用以下方法
HTResolveMethod *resolveMethod = [[HTResolveMethod alloc] init];[resolveMethod setupDatasWithTitle:@"我的日记"];
完整的消息转发
如果备援接收能未能处理选择子,会调用- (NSMethodSignature )methodSignatureForSelector:(SEL)aSelector生成方法签名,然后系统用这个方法签名生成NSInvocation对象。NSInvocation对象包含选择子、目标及参数。之后调用- (void)forwardInvocation:(NSInvocation )anInvocation方法改变调用目标,使消息在新目标上得以调用。这种方法有两种实现方式:一种实现方式与调用备援接收者方法有异曲同工的作用,而越往后面处理消息的代价就越大,所以不推荐在此方法中实现类似效果。另一种实现方式是改变消息内容或是改变选择子。
新建HTResolveMethod类
①HTResolveMethod.h
#import@interfaceHTResolveMethod:NSObject- (void)setupDatasWithTitle:(NSString*)title;@end
②HTResolveMethod.m
#import"HTResolveMethod.h"#import"HTForwardingTarget.h"@implementationHTResolveMethod/**
* 第二歩:进入消息转发流程重定向
* 将setupDatasWithTitle:转发到HTForwardingTarget类
*/- (id)forwardingTargetForSelector:(SEL)aSelector { NSString *selectorStr = NSStringFromSelector(aSelector);if([selectorStr isEqualToString:@"setupDatasWithTitle:"]) { HTForwardingTarget *forwardingTarget = [HTForwardingTargetnew];returnforwardingTarget; }return[superforwardingTargetForSelector:aSelector];}@end
新建HTForwardingTarget类
③HTForwardingTarget.m
//第三步,生成方法签名,然后系统用这个方法签名生成NSInvocation对象- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSString *selectedStr = NSStringFromSelector(aSelector);if([selectedStr isEqualToString:@"setupDatasWithTitle:"]) { NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:@"];returnsign; }return[super methodSignatureForSelector:aSelector];}//第四步,改变选择子- (void)forwardInvocation:(NSInvocation *)anInvocation { HTForwardInvocation *forwardInvocation =[[HTForwardInvocation alloc] init];
anInvocation.selector = NSSelectorFromString(@"setMsg:");
if ([forwardInvocation respondsToSelector:[anInvocation selector]]) { [anInvocation invokeWithTarget:forwardInvocation]; }else{ [super forwardInvocation:anInvocation]; }}
新建HTForwardInvocation类
④HTForwardInvocation.m
-(void)setMsg:(NSString*)msg { NSLog(@"选择子被改变了,%@",msg);}
⑤在ViewController.m中调用以下内容
HTResolveMethod *resolveMethod = [[HTResolveMethod alloc] init];[resolveMethod setupDatasWithTitle:@"我的日记"];
如果最终方法仍未实现,则调用NSObject的- (void)doesNotRecognizeSelector:(SEL)aSelector方法抛出异常
那么问题来了,知道动态方法解析、消息转发机制有什么用呢?我们举个简单的小例子:
新建HTResolveMethod类
①HTResolveMethod.h
#import/** 声明协议,当HTResolveMethod或其子类自定义方法未实现时,保证程序不崩溃 ,弹出提示框,并在控制台输出未实现的方法*/@protocolResolveMethodCrashDelegate - (void)resolveMethodCrashWithSelName:(NSString *)selName;@end@interfaceHTResolveMethod : NSObject@property(nonatomic, weak) id crashDelegate;@end
②HTResolveMethod.m
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];returnsign;}- (void)forwardInvocation:(NSInvocation *)anInvocation { [superforwardInvocation:anInvocation];}- (void)doesNotRecognizeSelector:(SEL)aSelector {NSString*selectedStr = NSStringFromSelector(aSelector); [selfcrashHandle:selectedStr];}- (void)crashHandle:(NSString*)selName {if(self.crashDelegate&& [self.crashDelegaterespondsToSelector:@selector(resolveMethodCrashWithSelName:)]) { [self.crashDelegateresolveMethodCrashWithSelName:selName]; }}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
新建HTResolveSonMethod类(只声明不实现)
③HTResolveSonMethod.h
#import"HTResolveMethod.h"@interfaceHTResolveSonMethod:HTResolveMethod- (void)tapNextButtonWithTag:(NSInteger)tag;@end
④ViewController.m中调用以下方法
#import"ViewController.h"#import"HTResolveSonMethod.h"@interfaceViewController()@end@implementationViewController- (void)viewDidLoad { [superviewDidLoad];self.view.backgroundColor= [UIColorpurpleColor];UIButton*button = [UIButtonbuttonWithType:UIButtonTypeCustom]; button.frame= CGRectMake(30,100, CGRectGetWidth(self.view.bounds)-60,30); [button setTitle:@"准备崩溃"forState:UIControlStateNormal]; [button addTarget:selfaction:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:button];}- (void)buttonClick:(id)sender { HTResolveSonMethod *resolveSonMethod = [[HTResolveSonMethod alloc] init]; resolveSonMethod.crashDelegate=self; [resolveSonMethod tapNextButtonWithTag:4];}#pragma ResolveMethodCrashDelegate- (void)resolveMethodCrashWithSelName:(NSString*)selName { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示信息"message:[NSStringstringWithFormat:@"崩溃方法:%@",selName] preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消"style:UIAlertActionStyleCancel handler:nil]; [alertController addAction:cancelAction]; [selfpresentViewController:alertController animated:YEScompletion:nil];NSLog(@"此方法不存在selName==%@",selName);}@end
效果如图:
demo地址:https://github.com/hantengteng/HTMessageTransmit
转载https://blog.csdn.net/xiaozhuanddapang/article/details/62336072