RunTime

什么是runtime?

Runtime库主要做下面几件事:

  1. 封装:在这个库中,对象可以用C语言中的结构体表示而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
  2. 找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。这将在后面详细介绍。
  • RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。
  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数
  • 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
  • 事实证明:
    • 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
    • 在编译阶段,C语言调用未实现的函数就会报错。

我们写的oc代码,它在运行的时候也是转换成了runtime方式运行的,更好的理解runtime,也能帮我们更深的掌握oc语言。 每一个oc的方法,底层必然有一个与之对应的runtime方法。

  • 当我们用OC写下这样一段代码 [tableView cellForRowAtIndexPath:indexPath];

  • 在编译时RunTime会将上述代码转化成[发送消息] objc_msgSend(tableView, @selector(cellForRowAtIndexPath:),indexPath);

常用的头文件

#import <objc/runtime.h> 包含对类、成员变量、属性、方法的操作
#import <objc/message.h> 包含消息机制

常用方法

class_copyIvarList()返回一个指向类的成员变量数组的指针
class_copyPropertyList()返回一个指向类的属性数组的指针

注意:根据Apple官方runtime.h文档所示,上面两个方法返回的指针,在使用完毕之后必须free()。

ivar_getName()获取成员变量名-->C类型的字符串
property_getName()获取属性名-->C类型的字符串
-------------------------------------
typedef struct objc_method *Method;
class_getInstanceMethod()
class_getClassMethod()以上两个函数传入返回Method类型
---------------------------------------------------
method_exchangeImplementations()交换两个方法的实现

常见作用

  • 动态的添加对象的成员变量和方法

  • 动态交换两个方法的实现

  • 拦截并替换方法

  • 在方法上增加额外功能

  • 实现NSCoding的自动归档和解档

  • 实现字典转模型的自动转换

代码实现

要使用runtime,要先引入头文件#import 这些代码的实例有浅入深逐步讲解,最后附上一个我在公司项目中遇到的一个实际问题。

1. 动态变量控制

在程序中,xiaoming的age是10,后来被runtime变成了20,来看看runtime是怎么做到的。

  1. 动态获取XiaoMing类中的所有属性[当然包括私有]

Ivar *ivar = class_copyIvarList([self.xiaoming class], &count);

  1. 遍历属性找到对应name字段

const char *varName = ivar_getName(var);

  1. 修改对应的字段值成20

object_setIvar(self.xiaoMing, var, @"20");

  1. 代码参考

    -(void)answer{
      unsigned int count = 0;
      Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
    
      for (int i = 0; i
    

    2.动态添加方法