iOS-了解Runtime
2017-09-08 本文已影响0人
a浮生若梦a
一.Runtime是什么?
Runtime也叫运行时态,是iOS底层用C语言函数和汇编语言封装的一套API,我们的程序在运行过程中,都是基于Runtime实现的。
二.Runtime的消息机制
//例如我们创建一个People类
People * pe = [[People alloc] init];
//通过objc_msgSend函数来发送消息,转换为:
id pe = objc_msgSend(objc_msgSend([Person class], @selector(alloc)), @selector(init));
//再通过objc_getClass和sel_registerName函数往下转换为:
id pe = objc_msgSend(objc_msgSend(objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
- 这就是消息发送机制。
- 苹果封装了消息机制,一般不建议大家使用底层的消息机制。
二.Runtime演示一
-
这里用OC的归档和解档来当例子,不知道可以查看前面我写的 iOS归档和解档
- 我们知道OC的序列化是把model转化为二进制存储,使用也很方便,但是如果一个model的属性很多的话,在写归档或者解档的时候 对我们来说就成了一种负担,这里就用到Runtime来解决。
-
下面进入主题
- 导入头文件(Runtime系统是具有公共接口的动态共享库。头文件存放于/usr/include/objc目录下,这意味着我们使用时只需要引入objc/Runtime.h头文件即可。)
1.导入头文件 #import <objc/runtime.h> 2.获取类成员变量列表,返回类的所有属性和变量 unsigned int count = 0; Ivar *ivars = class_copyIvarList([UIButton class], &count); //第一个参数填写类(这里写button),第二个参数count为类成员的数量 3.通过指针取出button数据 Ivar ivar = ivars[0]; (默认第一个可以通过循环获取每个数据) 4.获取数据name,返回C的字符串 const char *name = ivar_getName(ivar); //转换成OC字符串(这里获取到成员变量的name) NSString *nameStr = [NSString stringWithUTF8String:name]; 5.nameStr就是获取到button的一些私有变量
- 解档和归档用runtime来获取成员变量修改成如图所示(图中注释的三行为之前的原代码) 归档 解档
- 下面我删除app重新运行一下,ViewController里面写两个button,和两个对应的点击方法(归档方法和解档方法) 运行之后先点击一下解档按钮 此时控制台输出(解档方法里面有个输出) 发现输出数据都是null,Don't worry😺,这是因为还有没归档,所以解档是没有数据的。 接下来我点击一下归档,然后我再点击一下解档,这个时候就可以看到有输出信息了,说明用runtime改写成功。(是不是很简单呢😊)
-
小结:通过runtime可以获取到类的一些私有变量和私有方法。
二.Runtime演示二
-
首先我们都知道
NSURL * url = [NSURL URLWithString:@"这里填写请求的URL字符串"];
里面填写请求URL字符串,如果拼出来的字符串http://baidu.com?p1=%+&sd f&p2=这里有汉字
中有汉字、特殊符号&%和空格等,必须进行转译才能正确访问,这个时候就需要对 URL 进行 Encode。如果填写的URL里面有中文的话,返回的URL对象为nil(这里只举例包含汉字的URL访问)
那么我能不能封装一下,在调用方法内部的时候如果请求的URL包含文字就调用自己写的方法实现,如果不包含文字就调用系统原来的方法实现,我能不能截取到这个方法改成我自己的方法呢?这里我们就可以用Runtime来解决。- 首先创建一个URL的类别,CNSURL
- 在NSURL+CNSURL.m里面,重写一个CHURLWithString方法(isContainChinese方法用来判断是否包含中文)如图所示 上图中要注意方法里面调用本方法其实是调用NSURLWithString的方法实现,这一点一定要搞清楚。
-
通过进行方法交换来实现:
1.导入头文件 #import <objc/runtime.h> 2.写load方法,因为load方法在程序一进来的时候就开始执行,比main函数都早。 3.在load方法里 class_getClassMethod(类, 方法名) 用来获取类方法。 class_getInstanceMethod(类, 方法名) 用来获取实例方法。 method_exchangeImplementations(方法一, 方法二) 用来交换两个方法的实现。
- 在ViewController中实现两个按钮一个有文字URL,一个没有文字URL
- 点击第一个没有文字的URL,控制台输出
- 点击第二个有文字的URL,控制台输出
- 说明点击按钮的时候调用URLWithString方法(第一个输出打印的是判断文字的输出,然后打印出编码处理的输出,最后打印出上图中的一行输出) 其实是调用了NSURL+CNSURL里面的CHURLWithString方法。
-
小结:在OC的Runtime中任何方法的调用其内部都是发送消息,上面是通过发送消息来找到方法编号(SEL),通过方法编号来找到方法实现(IMP),这里把方法实现改成CHURLWithString这个的方法实现,这叫方法欺骗。