什么是运行时(runtime)?

编程语言有静态和动态之分。所谓静态语言,就是在程序运行前决定了所有的类型判断,类的所有成员、方法在编译阶段就确定好了内存地址。也就意味着所有类对象只能访问属于自己的成员变量和方法,否则编译器直接报错。比较常见的静态的语言如:java,c++,c等等。

动态语言,恰恰相反,类型的判断、类的成员变量、方法的内存地址都是在程序的运行阶段才最终确定,并且还能动态的添加成员变量和方法。也就意味着调用一个不存在的方法时,编译也能通过,甚至一个对象它是什么类型并不是表面我们所看到的那样,只有运行之后才能决定其真正的类型。相比于静态语言,动态语言具有较高的灵活性和可订阅性。而oc,正是一门动态语言。

所谓运行时,就是程序在运行时做的一些事。苹果提供了一套纯c语言的api,即runtime。在iOS开发中runtime的特性使得oc这门语言具有独特的魅力,可以利用运行时处理一些特殊的事情。要了解运行时,首先需要了解oc的消息机制。

什么是消息机制?

在Objective-C中,任何方法的调用,本质是发送消息。比如下面方法:

[obj  method];

被编译器转化为:

objc_msgSend(obj, @selector (method));

也就是说在oc中调用任何一个方法,其实质是转换为runtime中的一个函数objc_msgSend(),这个函数的作用是向obj对象(方法的调用者)发送了一条消息,告诉它你该去执行某个方法。所以,也可以直接用运行时去调用任何方法:

如:
    
Dog *dog = [[Dog alloc] init];
[dog run:50];

等价于
Dog *dog = objc_msgSend(objc_msgSend(objc_getClass("Dog"), @selector(alloc)), @selector(init));
objc_msgSend(dog, sel_registerName("run:"),50);//调用带参数的方法

现在可以解释oc消息机制,也就是一个方法的调用流程:
1、编译器会先将代码[obj method]转化为objc_msgSend(obj, @selector (method))函数去执行。
2、在objc_msgSend()函数中,首先通过obj的isa指针找到(对象)obj对应的(类)class。
3、在class中会先去cache中通过SEL查找对应函数method(cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若cache中未找到。再去class中的消息列表methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

补充:在oc中,每一个对象都有一个isa指针变量,这个指针指向的是对象的类,我们可以通过isa指针访问一个对象的类方法都保存在类的消息列表中,这个列表其实是一个字典,key是selector,value是IMP(imp是一个指针类型,指向方法的实现),并且selector和IMP之间的关系是在运行时才决定的,而不是编译时。如此们就可以做出一些特别事情来。

runtime作用

  • 利用runtime运行时,在程序运行过程中,动态创建一个类。

  • 利用runtime运行时,在程序运行过程中,动态修改一个类的属性、方法。

  • 利用runtime运行时,在程序运行过程中,遍历一个类的所有属性和方法。

备注:

  • runtime头文件 #import <objc/message.h>包含 #import <objc/runtime.h>
  • Xcode 5开始,苹果不建议调用底层方法,使用objc_msgSend会报错,解决办法是将Enable Strict Checking of objc_msgsend calls值设置为NO。

posted on 2018-10-08 16:36  广坤山货  阅读(217)  评论(0编辑  收藏  举报