iOS Runtime(一)
来源:互联网 发布:国画淘宝梅花挂客厅 编辑:程序博客网 时间:2024/05/20 08:22
什么是Runtime
根据字面意思,可以解释为程序运行时,是oc的底层实现,那么Runtime具体是什么样呢?
- 首先,看一下下面的代码
Person *p=[Person alloc]init];
这是我们经常使用的实例化对象的方法,那么,底层是怎么实现的呢?可以进行这样的拆分
Person * p = [Person alloc];p = [p init];//[p eat];[p performSelector:@selector(eat)];
首先如果没有实现eat方法[p eat]
是会报错的,但是[p performSelector:@selector(eat)]
不会,编译器认为运行时会动态添加该方法,所以没有实现也不会报错,这位动态添加方法打下了基础。然后,实例化的底层是怎么实现的,这就用到了Runtime。
Person * p = objc_msgSend(objc_getClass("Person"), @selector(alloc));p = objc_msgSend(p, @selector(init));objc_msgSend(p, @selector(eat));
那么,用clang转换为c语言后这句又是怎么实现的呢,其实跟这个几乎一样,就是利用Runtime
Person * p = ((HKPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((HKPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("HKPerson"), sel_registerName("alloc")), sel_registerName("init"));
这样,将oc语言转换为c函数,这就是Runtime。
Runtime有什么用
- 方法交换
oc语言抛异常能力比较差,往往在代码量大的时候,检查错误比较头疼。例如
//现在我需要给URL做检测!!NSURL * url = [NSURL URLWithString:@"www.baidu.com/中文"];//NSURLRequest * request = [NSURLRequestrequestWithURL:url];//发送了NSLog(@"%@",request);
这样是可以正常发送请求的,即使url为null
也可以发送请求。在代码量大的时候,这样的错误是很难检查出来的。那么,怎么办呢?可以新建一个NSURL
的分类,添加一个URLWithString:
方法,但是,难道每次使用这个方法都要导入一次头文件,这样非常繁琐。有没有什么简单的方法呢,可以不可以修改系统的方法呢?当然可以,这就要用到Runtime了。
新建一个分类写一个自己的方法, 方法中加上对url的判断:
+(instancetype)WRQ_URLWithString:(NSString *)URLStirng{ NSURL * url = [NSURL URLWithString:URLStirng];//调用了系统的方法了 if (url == nil) { NSLog(@"哥么这个url是空"); } return url;}
然后,就要利用Runtime交换方法了。在此之前,先明确一个问题,大家知道在app启动后,什么地方最早执行么?大多数人可能会想到main()
函数,对,这是程序的主入口,但是,在加载函数之前,会先加载文件,即调用+load()
方法,为了让修改后的方法对任何地方都适用,就要将交换方法写在load中。
+(void)load{ //class_getInstanceMethod:获取对象方法 //class_getClassMethod:获取类方法 Method URLWithStr = class_getClassMethod([NSURL class], @selector(URLWithString:)); Method HKURLWithStr = class_getClassMethod([NSURL class], @selector(HK_URLWithString:)); //交换 method_exchangeImplementations(URLWithStr, HKURLWithStr);}
这样,就完成了两个方法的交换,但是,运行程序,会进入死循环,这是为什么呢?因为交换方法后,URLWithString:
实际上执行的是WRQ_URLWithString:
这样就自己调用了自己,无限递归。应该改为
+(instancetype)WRQ_URLWithString:(NSString *)URLStirng{ NSURL * url = [NSURL WRQ_URLWithString:URLStirng];//调用了系统的方法了 if (url == nil) { NSLog(@"哥么这个url是空"); } return url;}
这样,就可以达到我们想要的效果,只要调用系统方法,就会检查url是否为空,成功地修改了系统方法。
- 动态添加方法
说到动态添加方法,就又要说到消息转发机制。这里主要讲动态添加方法,就简单地说说。在调用了[p performSelector:@selector(eat)]
方法或者objc_msgSend(p, @selector(eat))
后,系统会在类中寻找该方法,如果没有找到就会调用+(BOOL)resolveInstanceMethod:(SEL)sel
或者+ (BOOL)resolveClassMethod:(SEL)sel
方法,如果return NO;
就会继续调用其他函数,给你几次添加方法的机会,直到最后都没有添加方法程序就会崩溃。当然,动态添加方法最好的时机就是调用这两个方法的时候。
+ (BOOL)resolveInstanceMethod:(SEL)sel{ if (sel == @selector(eat)) { class_addMethod([self class], sel, (IMP)eat, "v@:"); return YES; } return [super resolveInstanceMethod:sel];}void eat(id self,SEL _cmd){ NSLog(@"我已经吃了....");}
- 函数参数
- __unsafe_unretained Class cls
这是向哪个类中添加该方法 - SEL name
这是函数名称 - IMP imp
这是一个函数指针,指向下面的c函数 - const char *types
这是函数类型,每个c函数都有两个隐藏参数(id self,SEL _cmd)
一个是方法调用者,一个是方法编号,具体可以看官方帮助文档。
- __unsafe_unretained Class cls
- IOS Runtime(一)
- iOS Runtime(一)
- iOS Runtime总结(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- iOS RunTime 机制浅析(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- IOS高级开发~Runtime(一)
- iOS高级开发~Runtime(一)
- iOS中的runtime入门(一)
- iOS runtime(一)
- iOS Runtime应用实例(一)类别添加属性
- iOS运行时(runtime)探究一:重要概念
- iOS Runtime应用实例(一)类别添加属性
- STM32 单片机控制无源蜂鸣器唱歌 欢乐颂
- 如何在Windows下像Mac一样优雅开发?
- 设计模式之组合模式
- 【LeetCode】53.Maximum Subarray最大连续子序列和
- 个人的中小型项目前端架构浅谈
- iOS Runtime(一)
- 阿里云SMS发短信python3代码
- [leetCode刷题笔记]516. Longest Palindromic Subsequence
- hdu4727-(The Number Off of FFF)
- 保障MySQL安全的十四个最佳方法
- 一. Scala安装与环境配置
- 位运算符 和MySQL运算符的优先级
- maven build 无反应,直接terminated
- Junit4-使用JUnit4