iOS之RunTime数据结构

由上面一章中,我们了解了什么是RunTimeRunTime用来做什么,下面了解一下Runtime数据结构。

我们知道在Objective-C中,使用[object doSomething]语法并不会马上执行object接受者对象的doSomething方法的代码,而是向object接受者对发送一条doSomething消息,这条消息可能由object接受者对来处理,也可能由转发给其他对象来处理,也有可能假装没有接收到这条消息而没有处理。

其实[object doSomething]被编译器转化为:

id objc_msgSend ( id self, SEL op, ... );
复制代码

下面从两个数据结构idSEL来逐步分析和理解Runtime有哪些重要的数据结构。

id

objc_msgSend方法里面的第一个参数的数据类型id,通用类型指针,能够表示任何对象。

查看源文件,可以看出id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员,根据Class isa指针就可以找到对象所属的类。

Class

从源文件看出,Objective-C的对象就是一个包含isa指针的数据结构,而isa指针的数据类型是ClassClass表示对象所属的类。

从源文件看出,Class其实就是一个objc_class结构体指针。objc_class结构体定义如下:

isa:在Objective-C中,所有的类自身也是一个对象,即类对象。在这个类对象里面也有一个isa指针,它指向metaClass(元类)。
super_class:指向该类的父类,如果该类已经是最顶层的根类(如NSObject),则super_class为NULL。
name:这个类的类名。
version:提供类的版本信息,这对于对象的序列化非常有用,它可是让我们识别出不同类定义版本中实例变量布局的改变。
info:类信息,供运行期使用的一些位标识。
instance_size:该类的实例变量大小。
ivars:该类的成员变量链表。
methodLists:方法定义的链表。
protocols:协议链表。
cache:一个接收者对象接收到一个消息时,它会根据isa指针去查找能够响应这个消息的对象。在实际使用中,这个对象只有一部分方法是常用的,很多方法其实很少用或者根本用不上。这种情况下,如果每次消息来时,我们都是methodLists中遍历一遍,性能势必很差。这时,cache就派上用场了。在我们每次调用过一个方法后,这个方法就会被缓存到cache列表中,下次调用的时候runtime就会优先去cache中查找,如果cache没有,才去methodLists中查找方法。这样,对于那些经常用到的方法的调用,但提高了调用的效率。
复制代码

注意:

  • 在面向对象设计中,一切都是对象,Class在设计中本身也是一个对象。
  • 由此可见,结构体objc_class也是继承objc_object,说明Class在设计中本身也是一个对象。
元类(Meta Class)

Objective-C中,所有的类自身也是一个对象,这个对象里面也有一个isa指针,它指向metaClass(元类),向这个对象发送消息(即调用类方法)。

从图中看出:

  • 当我们向一个对象发送消息时,isa指针会在这个对象所属的这个类的方法列表中查找方法;
  • 向一个类发送消息时,isa指针会在这个类的meta-class的方法列表中查找。meta-class之所以重要,是因为它存储着一个类的所有类方法。
  • 每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己,这样就形成了一个完美的闭环。

SEL

objc_msgSend函数第二个参数类型为SEL,它是selectorObjc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的id,而这个id的数据结构是SEL,即表示一个方法的selector的指针。

  • 方法的selector用于表示运行时方法的名字,Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(int类型的地址),这个标识就是SEL
  • Objective-C中,只要方法名相同,那么方法的SEL就是一样的,每一个方法都对应着一个SEL,所以在Objective-C中,同一个类中或者这个类的继承体系中,不能存在2个同名的方法,不同的类可以拥有相同的selector,不同的类的实例对象执行相同的selector,会在各自的方法列表中根据selector去寻找对应的IMP
  • 在本质上,SEL只是一个指向方法的指针(被hash化得KEY值),能提高方法的查询速度。
IMP

IMP就是implementation的缩写,本质就是一个函数指针,这个被指向的函数包含一个接收消息的对象id,调用方法的SEL,以及一些方法参数,并返回一个id。因此我们可以通过SEL获得它所对应的IMP,在取得了函数指针之后,也就意味着我们取得了需要执行方法的代码入口,这样我们就可以像普通的C语言函数调用一样使用这个函数指针。

SEL就是为了查找方法的最终实现IMP的,由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的IMP

Method

Method是一种代表类中的某个方法的类型。

objc_method在上面的方法列表中提到过,它存储了方法名,方法类型和方法实现:

注意:

  • 方法名类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同。
  • 方法类型method_types是个char指针,其实存储着方法的参数类型和返回值类型。
  • method_imp指向了方法的实现,本质上是一个函数指针。
Ivar

Ivar是一种代表类中实例变量的类型。

Cache

Cache其实就是一个存储Method的链表,主要是为了优化方法调用的性能。

当对象receiver调用方法message时,首先根据对象receiverisa指针查找到它对应的类,然后在类的methodLists中搜索方法,如果没有找到,就使用super_class指针到父类中的methodLists查找,一旦找到就调用方法。如果没有找到,有可能消息转发,也可能忽略它。但这样查找方式效率太低,因为往往一个类大概只有20%的方法经常被调用,占总调用次数的80%。所以使用Cache来缓存经常调用的方法,当调用方法时,优先在Cache查找,如果没有找到,再到methodLists查找。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值