iOS runtime 消息转发
来源:互联网 发布:鸟哥linux基础篇 编辑:程序博客网 时间:2024/06/05 05:34
作者:Love@YR
链接:http://blog.csdn.net/jingqiu880905/article/details/50897011
请尊重原创,谢谢!
首先看个图:(抠自:http://www.cocoachina.com/ios/20160301/15494.html)
文章参考:http://blog.csdn.net/hyugahinat/article/details/50688333
还有:http://blog.csdn.net/i_am_jojo/article/details/44704633
下面看一下测试的代码:
ViewController.m中:
//// ViewController.m// TestRuntime//#import "ViewController.h"#import <objc/runtime.h>@interface ViewController ()@endIMP orginIMP;//全局函数NSString * MyUppercaseString(id SELF, SEL _cmd){ NSLog(@"begin uppercaseString"); NSString *str = orginIMP(SELF,_cmd); //若此处出现 too many arguments to function call的错误,把build setting 里的enable strict checking of objc_msgSend calls改为No NSLog(@"end uppercaseString"); return str;}void dynamicMethodIMP (id self,SEL _cmd, NSString *str){ NSLog(@"do something sel, argument is %@",str);}@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self performSelector:@selector(doSomething:) withObject:@"hi,how are you?"]; [self testReplaceMethod]; [self performSelector:@selector(secondVCMethod:withBoolValue:) withObject:[NSNumber numberWithInteger:10] withObject:[NSNumber numberWithBool:NO]];}//第一个机会:动态添加一个实例方法给本类,类方法的话用的是resolveClassMethod+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(doSomething:)) { NSLog(@"add method here"); class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:@"); //双击选中class_addMethod方法,xcode的导航栏右侧出现quick help,点击parameters里的Type encodings进入苹果开发文档可以查阅"v@:"代表的意思 return YES; } return [super resolveInstanceMethod:sel];}//方法替换,(替换系统的方法)-(void)testReplaceMethod{ Class strcls = [NSString class]; SEL oriUppercaseString = @selector(uppercaseString); orginIMP = [NSString instanceMethodForSelector:oriUppercaseString]; IMP imp2 = class_replaceMethod(strcls,oriUppercaseString,(IMP)MyUppercaseString,NULL); NSString *s = @"hello world"; NSLog(@"%@",[s uppercaseString]);}//第二个机会,替换消息的接受者为其他对象//返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程。- (id)forwardingTargetForSelector:(SEL)aSelector { Class class = NSClassFromString(@"SecondViewController"); UIViewController *vc = class.new; //下面3种if都行 if ([vc respondsToSelector:aSelector]) { //if (aSelector==NSSelectorFromString(@"secondVCMethod:withBoolValue:")) { //if (aSelector==@selector(secondVCMethod:withBoolValue:)) { NSLog(@"sencondvc do this!"); return vc; } return nil;}//第三个机会,当第二个返回nil时执行//这个函数和后面的forwardInvocation:是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行//真正执行从methodSignatureForSelector:返回的NSMethodSignature。在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能以Selector的形式转向一个对象)- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{ Class class = NSClassFromString(@"SecondViewController"); UIViewController *vc = class.new; if (aSelector==NSSelectorFromString(@"secondVCMethod:withBoolValue:")) { NSMethodSignature *signature=[super methodSignatureForSelector:aSelector]; if (!signature) { signature=[vc methodSignatureForSelector:aSelector]; NSUInteger numberOfArguments = signature.numberOfArguments; for (NSUInteger i = 2; i < numberOfArguments; i++) {//secondVCMethod:withBoolValue:的方法, signature的return type 为v,参数为@:qB,所以从第3个字母开始才是参数的真正类型. const char *argumentType = [signature getArgumentTypeAtIndex:i]; NSLog(@"%c",argumentType[0]); } } return signature; } return nil;}//该消息的唯一参数是个NSInvocation类型的对象——该对象封装了原始的消息和消息的参数。我们可以实现forwardInvocation:方法来对不能处理的消息做一些默认的处理,也可以将消息转发给其他对象来处理,而不抛出错误。//这里需要注意的是参数anInvocation是从哪的来的呢?其实在forwardInvocation:消息发送前,Runtime系统会向对象发送methodSignatureForSelector:消息,并取到返回的方法签名用于生成NSInvocation对象。所以我们在重写forwardInvocation:的同时也要重写methodSignatureForSelector:方法,否则会抛异常。- (void)forwardInvocation:(NSInvocation *)anInvocation{ Class class = NSClassFromString(@"SecondViewController"); UIViewController *vc = class.new; if ([anInvocation selector]==NSSelectorFromString(@"secondVCMethod:withBoolValue:")) { if ([vc respondsToSelector: [anInvocation selector]]) [anInvocation invokeWithTarget:vc]; else [super forwardInvocation:anInvocation]; }}- (void)doesNotRecognizeSelector:(SEL)aSelector{ NSLog(@"throw a exception");}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}@end
其中secondVCMethod方法被转发给SecondViewController,实现如下:
-(void)secondVCMethod:(NSInteger)number withBoolValue:(BOOL)bovalue{ NSString *str1=[NSString stringWithFormat:@"This is secondVC method!,number is %@, and bo value is %@",@(number),@(bovalue)]; NSLog(@"str1");}
上述代码对此图做了说明
首先ViewController里找不到secondVCMethod方法,会自动调用resolveInstanceMethod,如果返回不是yes则会调用forwardingTargetForSelector,如果此方法返回的为nil,则会调用methodSignatureForSelector,此方法返回nil的话直接走到doesNotRecognizeSelector抛异常,此方法不为nil的话调用forwardInvocation
而class_addMethod class_replaceMethod 方法则是动态的添加一个方法或者替换一个方法的实现。
根据Demo实验,resolveInstanceMethod函数返回的BOOL值系统实现的objc_msgSend函数并没有参考,无论返回什么系统都会尝试再次用SEL找IML,如果找到函数实现则执行函数。如果找不到继续其他查找流程。所以会发现调用两次。
- iOS runtime 消息转发
- iOS runtime 之消息转发
- iOS runtime学习之消息转发机制
- ios runtime IMP指针 消息转发机制
- iOS Runtime与消息转发机制
- iOS 【iOS Runtime浅析(2):消息转发】
- Runtime之消息转发
- runtime基础、消息转发
- Runtime消息转发机制
- Runtime消息转发机制
- ios runtime IMP指针 消息转发机制Demo
- iOS运行时(runtime)探究三:消息转发
- 通过Runtime探讨iOS的消息转发机制
- Runtime系列(消息转发)
- runTime 的消息转发机制
- iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)
- RxSwift Runtime分析(利用OC消息转发实现IOS消息拦截)<原理同ReactiveCocoa>
- iOS Runtime详解(消息机制,类元对象,缓存机制,消息转发)
- Android Studio如何同NDK集成
- 如果需要修改表格,在eclipse 中需要做什么操作
- Android Volley完全解析(四),从源码的角度理解Volley
- 胃疼
- 盒子模型
- iOS runtime 消息转发
- Android 开发过程中需要注意的细节(二)
- POJ 2823 线段树简单操作
- 有那些UbuntuColors?
- MySQL5.5中文支持
- 蓝桥杯常用算法知识点:【递归】求n个元素的全排列
- Android进阶笔记(二)Handler消息机制理解
- 第二场
- ubuntu下pip安装xgboost