iOS运行时Runtime浅析

本文深入探讨了iOS的Runtime概念,解释了它是如何将OC代码转换为C代码执行的。Runtime允许在运行时动态创建类、添加属性和方法,以及遍历类信息。文章还介绍了Modern和Legacy Runtime的区别,以及如何通过Runtime接口获取属性、方法、成员变量和协议列表。重点讨论了方法调用过程、拦截调用的四个关键方法,以及如何动态创建和销毁类与对象。最后提到了类与元类的关系、类操作函数、实例操作函数以及成员变量和属性的相关操作。
摘要由CSDN通过智能技术生成

       运行时是iOS中一个很重要的概念,iOS运行过程中都会被转化为runtime的C代码执行。例如[target doSomething];会被转化成objc)msgSend(target,@selector(doSomething))来执行。这篇博客会较为全面的来讲解下Runtime。

       OC是一门动态语言,它将很多静态语言在编译和链接时做的事放到了运行时来处理。这种动态语言的优势在于:写代码能更加灵活,可以把消息转发给想要的对象,或者随意交换一个方法的实现。

       OC Runtime目前有两个版本:Modern Runtime和Legacy Runtime。Modern Runtime覆盖了64位的App,Legacy Runtime使用早期的32位App,所以现在可以不用管了。

(1)当我们需要使用Runtime的接口时,需要导入头文件:#import <objc/runtime.h>,Runtime可以进行如下操作,在运行时来获取当前类中的一些信息:

获取属性列表:



获取方法列表:



获取成员变量列表:



获取协议列表:



所以,我们大概可以总结出Runtime的作用:

  • 程序运行中,动态创建一个类;
  • 程序运行中,动态为某个类添加属性/方法,修改属性/方法;
  • 遍历一个类的所有成员变量/属性/方法;


(2)方法调用:

     如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象,没错,其实类也是一个对象)操作。如果调用的是类方法,就会到类的isa指针指向的对象(元类对象)中操作。查找过程如下:

  • 首先在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行;
  • 如果没有找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行;
  • 如果没找到,去父类指针所指向的对象中执行以上步骤;
  • 以此类推,如果一直到根类还没有找到,转向拦截调用;
  • 如果没有重写拦截调用的方法,程序报错;

说明下,重写父类的方法,其实并没有覆盖掉父类的方法,只是在当前类对象中找到这个方法后就不会再去父类中找了。如果想调用已经重写过的方法的父类实现,只需使用super这个编译器标志,它会在运行时跳过在当前类对象中寻找方法的过程。


(3)拦截调用

     拦截调用就是在找不到调用的方法程序崩溃之前,有机会通过重写NSObject的四个方法来处理:



以下两个方法需要转发到其他类处理:



  • 第一个方法是调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,可以加上自己的处理后返回YES;
  • 第二个方法和第一个方法类似,处理的是实例方法;
  • 第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要返回一个有这个方法的target;
  • 第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法;

(4)Runtime的常用之处就是可以在运行时动态添加方法。我们在上面讲到了拦截调用,动态添加方法可以和拦截调用结合起来使用。我们可以根据传递进来的SEL类型的selector动态添加一个方法。这样就算我们调用一个还没定义的方法程序也不会crash了。
.

其中class_addMethod的四个参数分别是:
  • Class cls :给哪个类添加方法;
  • SEL name:方法选择器selector;
  • IMP imp:方法的实现,C方法的实现可以直接获得。如果是OC方法,可以用+(IMP)instanceMethodForSelector获得方法的实现;
  • const char *types:方法的签名


(5)关联对象
      现在准备使用一个系统的类,你需要额外添加一个属性。我们可以使用继承。但是只增加一个属性,就去继承一个类,是比较浪费的。这时我们就可以使用Runtime的关联属性。

 先定义一个全局变量,用它的地址作为关联对象的key:


设置关联对象:




objc_setAssociatedObject的四个参数:
  • id object:给谁设置关联对象;
  • const void *key:关联对象唯一的key,就是上面定义的全局变量;
  • id value:关联对象;
  • objc_AssociatedPolicy:关联策略:





objc_getAssociatedObject的两个参数:
  • id object:获取谁的关联对象;
  • const void *key:根据这个唯一的key获取关联对象;

(6)方法交换
    将两个方法的实现交换。例如,将A方法和B方法交换,调用A方法的时候,就会执行B方法中的代码。反之亦然。我们一般在类的Category中实现以下代码:




(7)Runtime原理与机制
     Runtime运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。同时Runtime是一套比较底层的C语言API,属于一个C语言库,包含了很多底层的C语言API。程序运行时,最终都是转成了Runtime的C语言代码。
OC实现动态调用:
[obj makeText];

在编译时Runtime会将代码转化为:
objc_msgSend(obj,@selector(makeText));

obj对象有一个isa指针,




所有metaclass中isa指针都指向根metaclass,而根metaclass则指向自身。Root metaclass是通过继承Root Class产生的,与root class结构体成员一致。
@selector(makeText):这是一个SEL方法选择器。SEL主要作用是快速通过方法名字(makeText)查找到对应方法的函数指针,然后调用函数。SEL本身是一个int类型的地址,地址中存放着方法的名字。对于一个类中,每一个方法对应着一个SEL,所以iOS中不能存在2个名称相同的方法,即使参数类型不同。

动态查找过程:
      编译器将代码[obj makeText];转化为objc_msgSend(obj,@selector(makeText));在objc_msgSend函数中,首先通过obj的isa指针找到obj对应的class。在Class中先去cache中通过SEL查找对应函数method,若cache中未找到,再去methodList中查找,若methodList中未找到,则到superClass查找。若能找到,则将method加入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值