iOS开发------runtime之动态添加方法(动态决议,请求转发)
2016-02-28 16:13
471 查看
RunTime中实例变量调用方法的步骤:
1、在该实例变量的方法缓存列表中查找方法,如果找到执行.2、如果没找到,会去类结构中的相应方法列表中进行查找,如果找到执行.
3、如果方法列表没有找到该方法,那么就从父类中进行1、2部操作.
4、如果直到根类仍然没有找到方法,那么就会报错:unrecognized selector sent to instance 0x1005046c0.
也就是说
1、重写父类的方法,本质上不是覆盖了父类的方法,只不过是在本类中找到相应的方法,不再去父类中查找方法而已
2、super关键字并不是指父类,作用是 跳过此类直接从父类中进行查找方法
动态决议以及请求转发就是拦截上述过程,让其在runtime中运行相应的方法
动态决议
实现下面的两个方法//对类对象进行决议 + (BOOL)resolveClassMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); //对实例对象进行决议 + (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
返回值以及参数:
返回值:表示动态决议的成功与否
参数:sel 需要进行动态决议的方法
添加方法
/** * 运行时添加方法的函数 * * @param cls 需要动态决议的类 * @param name 需要动态决议的方法 * @param imp 需要执行方法的指针(本人理解为函数指针) * @param types 类型字符串,后面详细说 * * @return 添加的结果 */ (BOOL)class_addMethod(Class cls,SEL name,IMP imp,const char * types)
用代码来实现,更加简明,建立一个测试类RunTimeClass1:NSObject,不实现任何的方法:
在main中实现下面方法
RunTimeTextClass1 * class1 = [[RunTimeTextClass1 alloc]init]; [class1 performSelector:@selector(Text1) withObject:@"哈哈哈哈"];
因为类中根本没有这个方法,所以结果相信也都知道crash了,因为找不到这个方法的内存地址
RunTime[1047:62333] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[RunTimeTextClass1 Text1]: unrecognized selector sent to instance 0x100107540'
那么在RunTimeClass1中实现下面方法,需要导入头文件:<objc/runtime.h>
//实例变量的动态决议,类方法的动态决议是一样的 +(BOOL)resolveInstanceMethod:(SEL)sel { if ([NSStringFromSelector(sel) isEqualToString:@"Text1"]) { NSLog(@"RunTimeTextClass1 实例对象动态决议!"); class_addMethod([self class], sel, (IMP)test1, "v@:@"); } return [super resolveInstanceMethod:sel]; }
实现决议的方法test1,楼主使用C实现的,如果是用Objc实现的,可以通过方法:+(IMP)instanceMethodForSelector:(SEL)aSelector来转换,Objc的方法是至少有id self,SEL _cmd两个参数的普通C语言的函数,必须要有这两个参数
void test1(id self,SEL _cmd,NSString *str) { NSLog(@"%@",str); }
再来解释一下types这个字符串:
v :表示函数的返回值void
@:表示参数id self
: :表示SEL对象
@:表示后面的参数str:NSString
下面是苹果官方规定的符号:
接下来在运行一下,结果就不报错了,并执行了我们决议后的方法
请求转发
实现下面两个方法//调用不存在的方法的时候重定向到一个有该方法的对象 - (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); //将调用不存在的方法封装成NSInvocation对象传出,做完处理调用invokeWithTarget:方法触发 - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
举个例子来看:
在RunTimeTextClass1中调用-text1方法,但是在.h与.m中都没有实现这个方法,也不给他实现动态决议方法,调用方法text1
RunTimeTextClass1 * class1 = [[RunTimeTextClass1 alloc]init]; [class1 performSelector:@selector(text1)];
实现-forwardingTargetForSelector:方法来完成请求转发
然后在RunTimeTextClass1中实现第一个方法:-(id)forwardingTargetForSelector:(SEL)aSelector { return [[Text alloc]init]; }
下面是Text的实现文件
// // Text.m // RunTime // // Created by YueWen on 16/2/24. // Copyright © 2016年 YueWen. All rights reserved. // #import "Text.h" @implementation Text -(void)text1 { NSLog(@"我是Text!"); } @end
运行结果:运行了Text1的text1方法,如果Text1或者Text1父类中中没有实现该方发,那么程序依旧会crash
实现forwardingInvocation:方法来完成请求转发
首先实现这个方法:-(void)forwardInvocation:(NSInvocation *)anInvocation { Text * text = [[Text alloc]init]; SEL selector = anInvocation.selector; if ([text respondsToSelector:selector])//如果有这个方法 { [anInvocation invokeWithTarget:text];//触发方法 } else { [super forwardInvocation:anInvocation]; } }
但是用上面的方法来实现,还需要实现一个方法,为这个类对象进行方法签名:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { return [Text instanceMethodSignatureForSelector:aSelector]; }
这样再来看看效果,和上面的效果是一样的:
如果动态决议和请求转发都实现了,那么动态决议的优先级要高于请求转发.
相关文章推荐
- iOS自定义AlertView
- IOS开发日志之Xcode一些好用的插件大全
- kidd风的IOS日志之第三方-AFNetWorking3.0的最新用法
- kidd风的IOS日志之OC中initialize方法和init方法的区别
- iOS项目上线的流程
- IOS开发日志之RunLoop的原理和使用
- kidd风的IOS日志之第三方-SDWebImage的使用
- kidd风的IOS日志之Github上传代码详细教学
- iOS - 语音云通讯
- ios 消息推送流程 转载
- ios 解析html
- iOS7下隐藏statusbar
- ios-同步下载与异步下载
- iOS_SN_Xcode内存泄露调试
- ios 推送消息
- Foundation框架(7)时间操作
- iOS 9.0以后添加联系人到分组
- Foundation框架(6)数据处理对象
- iOS XML解析 - NSXMLParser
- iOS 运行时2