iOS RunTime 机制浅析(一)

最近在看某些书籍和博客的时候不时地有Runtime的出现,查了相关介绍之后觉得还是没有彻底地理解其机理,于是今天把官方文档给煲了一下,下面结合相关介绍,做一个相对全面的学习笔记。


Abstract

Runtime是Object-C特有的一种动态运行机制,compiler中的runtime system实现这种机制。

其主要在三方面实现:

第一是我们写的原代码,protocal、instance varible;

第二是Foundation Framework中的方法,如NSObject,NSProxy;

第三是直接使用plain C纯C语言的方法来实现。

接下来我们来看看这三方面分别是怎样一种实现机理。


第一

主要是指消息机制,最典型的就是我们所写代码中的函数调用,便是通过runtime system来实现的。

在介绍其实现过程之前我们先来剖析objc中的类变量是如何在内存中组织的。

在我们创建一个类的时候,系统会为这个类分配一段内存空间,用来存放其属性变量。而在这些属性变量的最开始,也就是内存的第一个空间,存放着每个类所特有的isa指针,如图fig-1.1。

fig-1.1
*isa
varible 1
varible 2
...

这个指针指向的是这个类的信息结构体class structures,结构体由两个变量组成,一个指向父类structures的指针还有一个指向类方法地址表格methor address table的指针,如图fig-1.2,图fig-1.3。由最小子类向上攀升,isa一层层向上指,直至NSObject基类。


fig-1.2
class structures
* superclass pointer 
* methor address list
fig-1.3
methor list
address 1
...
特别提示,isa指针也是runtime system所必须的。

有了类在内存中组织的结构的概念之后,我们再来分析我们所写代码的消息机制如何在runtime 中实现。

当我们在代码中调用某一函数,比如:

[object methor];
那么经runtime system时将会调用

objc_msgSend(receiver,selector);
来告知receiver调用selector函数。
再receiver类收到消息之后,其会启动一下过程寻找函数的实现IMP地址。

首先在私有内存空间中通过isa找到class structures,随后访问methor table,看是否有对应函数地址存在,如有便返回,如没有则通过super class structures pointer继续向上层的methor table寻找,直至寻找到对应的IMP地址或到NSObject都没找到则返回错误信息。假如找到对应的函数地址则调用并返回返回值,如图 fig-1.4。

fig-1.4

为了防止每次寻找地址空间耗费过多的时间,runtime system准备了一个cache table来存放已经找到的函数地址空间,当一个函数调用多次时便可以节省寻找时间,所以receiver 接收到消息之后应该是先在cache table中寻找,找不到时在执行后续的寻找,如下图fig-1.5。




以上便是objc中通过rumtime实现函数的调用过程。

值得一提的是在objc_msgSend函数中的receiver和selector函数实际上的隐藏参数,因为在objc层面其并没有直接显示出来。

不过这两个参数还是有作用的,特别是self参数,如下(引用自官方文档):

- strange
{
    id  target = getTheReceiver();
    SEL method = getTheMethod();
 
    if ( target == self || method == _cmd )
        return nil;
    return [target performSelector:method];
}
对于以上函数,我不是很理解,大家多多指教。

第二

runtime interact with methor in Foundation Framework这方面主要指的是NSObject提供的通过runtime system实现的函数,如description方法,其返回一段对于类的描述,最典型是用于debugge,比如数字类返回的是对于成员的描述。其他的诸如

 isKindOfClass:
isMemberOfClass:
respondsToSelector:
conformsToProtocol:
methodForSelector:
等通过runtime实现的包含在NSObject的函数,值得注意的是这些函数并不是objc函数,而是由runtime system提供的函数。

当我们在一个loop循环中持续调用某个函数时,这会频繁的通过runtime去寻找函数实现地址,如第一点中所提到的过程,此时circumvent dynamic binding,绕过runtime动态绑定而调用函数的方法便是使用methorforSelector:直接找到函数的地址,这样在多次的循环中,可以节省很多时间。


其次重要的方面便是通过runtime system进行方法的动态implementation实现。

对属性声明

@dynamic propertyName;

告诉编译器这个属性有关的方法是动态生成的,进而编写函数实现代码

void dynamicMethodIMP(id self, SEL _cmd) {
    // implementation ....
}
以及动态生成过程

@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL == @selector(resolveThisMethodDynamically)) {
          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
          return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}
@end

当检测到SEL是
resolveThisMethodDynamically

即动态实现函数时,便调用class_addMethod来动态实现。比较常见的便是coredata中关联类的属性声明。


动态加载机制为objc解决了很多事情,实现了很多功能,当类被创建时,协议被创建时我们都可以在运行时动态地添加相关功能module。Cocoa框架中有很多也是通过动态加载来实现的。


第二点最后一个内容是forwarding Message的由runtime system提供消息转发机制,关于剩下的部分在下一篇中会提到。未完待续。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值