iOS-了解Runtime

一.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"));
  • 这就是消息发送机制。
  • 苹果封装了消息机制,一般不建议大家使用底层的消息机制。

这是下面讲的Demo下载地址

二.Runtime演示一

  • 这里用OC的归档和解档来当例子,不知道可以查看前面我写的 iOS归档和解档

  • 我们知道OC的序列化是把model转化为二进制存储,使用也很方便,但是如果一个model的属性很多的话,在写归档或者解档的时候 对我们来说就成了一种负担,这里就用到Runtime来解决。
    iOS-了解Runtime_第1张图片
  • 下面进入主题

    • 导入头文件(Runtime系统是具有公共接口的动态共享库。头文件存放于/usr/include/objc目录下,这意味着我们使用时只需要引入objc/Runtime.h头文件即可。)
    1.导入头文件 #import 
    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来获取成员变量修改成如图所示(图中注释的三行为之前的原代码)
      iOS-了解Runtime_第2张图片
      归档

      iOS-了解Runtime_第3张图片
      解档
  • 下面我删除app重新运行一下,ViewController里面写两个button,和两个对应的点击方法(归档方法和解档方法)
    iOS-了解Runtime_第4张图片

    iOS-了解Runtime_第5张图片
    运行之后先点击一下解档按钮
    iOS-了解Runtime_第6张图片
    此时控制台输出(解档方法里面有个输出)
    iOS-了解Runtime_第7张图片

    发现输出数据都是null,Don't worry,这是因为还有没归档,所以解档是没有数据的。 接下来我点击一下归档,然后我再点击一下解档,这个时候就可以看到有输出信息了,说明用runtime改写成功。(是不是很简单呢)
    iOS-了解Runtime_第8张图片
  • 小结:通过runtime可以获取到类的一些私有变量和私有方法。

二.Runtime演示二

  • 首先我们都知道NSURL * url = [NSURL URLWithString:@"这里填写请求的URL字符串"];里面填写请求URL字符串,如果拼出来的字符串 http://baidu.com?p1=%+&sd f&p2=这里有汉字中有汉字、特殊符号&%和空格等,必须进行转译才能正确访问,这个时候就需要对 URL 进行 Encode。如果填写的URL里面有中文的话,返回的URL对象为nil(这里只举例包含汉字的URL访问)

    iOS-了解Runtime_第9张图片

    那么我能不能封装一下,在调用方法内部的时候如果请求的URL包含文字就调用自己写的方法实现,如果不包含文字就调用系统原来的方法实现,我能不能截取到这个方法改成我自己的方法呢?这里我们就可以用Runtime来解决。

    • 首先创建一个URL的类别,CNSURL
      iOS-了解Runtime_第10张图片
    • 在NSURL+CNSURL.m里面,重写一个CHURLWithString方法(isContainChinese方法用来判断是否包含中文)如图所示
      iOS-了解Runtime_第11张图片

      上图中要注意方法里面调用本方法其实是调用NSURLWithString的方法实现,这一点一定要搞清楚。
      iOS-了解Runtime_第12张图片
    • 通过进行方法交换来实现:

    1.导入头文件 #import 
    2.写load方法,因为load方法在程序一进来的时候就开始执行,比main函数都早。
    3.在load方法里 class_getClassMethod(类, 方法名) 用来获取类方法。
      class_getInstanceMethod(类, 方法名) 用来获取实例方法。
      method_exchangeImplementations(方法一, 方法二) 用来交换两个方法的实现。
    
    iOS-了解Runtime_第13张图片
    • 在ViewController中实现两个按钮一个有文字URL,一个没有文字URL
      iOS-了解Runtime_第14张图片

      iOS-了解Runtime_第15张图片
    • 点击第一个没有文字的URL,控制台输出
    • 点击第二个有文字的URL,控制台输出
      iOS-了解Runtime_第16张图片
    • 说明点击按钮的时候调用URLWithString方法(第一个输出打印的是判断文字的输出,然后打印出编码处理的输出,最后打印出上图中的一行输出) 其实是调用了NSURL+CNSURL里面的CHURLWithString方法。
  • 小结:在OC的Runtime中任何方法的调用其内部都是发送消息,上面是通过发送消息来找到方法编号(SEL),通过方法编号来找到方法实现(IMP),这里把方法实现改成CHURLWithString这个的方法实现,这叫方法欺骗。

你可能感兴趣的:(iOS-了解Runtime)