前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS RunTime之二:数据结构

iOS RunTime之二:数据结构

作者头像
s_在路上
发布2018-09-11 17:08:56
5450
发布2018-09-11 17:08:56
举报
文章被收录于专栏:iOS 开发杂谈iOS 开发杂谈

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

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

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

代码语言:javascript
复制
id objc_msgSend ( id self, SEL op, ... );

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

id

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

Paste_Image.png

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

Class

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

Paste_Image.png

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

代码语言:javascript
复制
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(元类),向这个对象发送消息(即调用类方法)。

Paste_Image.png

从图中看出:

  • 当我们向一个对象发送消息时,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的指针。

Paste_Image.png

  • 方法的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语言函数调用一样使用这个函数指针。

Paste_Image.png

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

Method

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

Paste_Image.png

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

Paste_Image.png

注意:

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

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

Paste_Image.png

Paste_Image.png

Cache

Paste_Image.png

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.09.19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • id
  • Class
  • 元类(Meta Class)
  • SEL
  • IMP
  • Method
  • Ivar
  • Cache
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档