关于objc_runtime的消息机制(二)

接上篇,我们已经大概的聊完了c++的虚函数实现机制。间接寻址体现在虚函数表的实现上。虚函数表由编译负责帮我们维护。我们来回头捋一捋函数调用过程的变化。在c中 函数名直接被译为函数指针(地址),调用的过程就是直接跳转到目的地址执行(当然,这个跳转不是普通的命令跳转,还包含着cpu寄存器状态的压栈 等等,不做细谈。)。到了c++中,对虚函数的调用就有了间接寻址了,这个函数的调用过程包括了:
. 1 根据对象地址找到对象内存区域
. 2 取出位于对象内存头部空间的虚函数表基址
. 3 查表 找到虚函数的具体地址
. 4 有了函数地址 然后就可以调用了。
总之 ,间接无非就是建立寻址表,将直接的地址翻译变为间接寻址。有了c++的虚函数其实已经可以实现面向对象中一个重要的概念:多态。但是 程序员是能够轻易满足的么?显然不是啊。这哪够?虚函数表是编译器负责维护和实现的,程序员并不能直接的操作和更改(其实是可以的,毕竟我们知道了对象的首段地址就是基地址表,我们可以通过更改这个表来实现,但是这些都不是C++设计者的本意,且这样的代码写出来可读性性极差,更别说什么维护了)。
这就牵扯出本文将提及的终极大杀器(苹果大法好,不过我还是喜欢巨硬。)object c 被称作动态语言,这个动态该如何解释呢?还是用c来做对比。c中每一个标识符(变量)都必须有一个类型,基本类型 int char float 等等,自定义类型 struct。这个类型一经声明,其实该对象在内存中的空间模型也就固定了(内存空间模型包括所占字节的大小,以及自定义结构体中,各个字段的内存区域分配)。object c 不是这样的语言。id object = *(赋值号右边的对象是什么类型就是什么类型。先记住这句话 一会儿就能体会到)。 简单的推测,id或许是一个指针类型,因为指针所占字节数是恒定不变的啊,不管什么类型的指针都可以进行相互赋值(将32个字节的struct 赋值给int,多出来的28个字节也没地搁啊,所以说指针是程序设计中最成功的概念之一)。于是我们跳转到id的定义看到这样的代码

typedef struct objc_object *id;

果不其然 id 是一个指向objc_object结构体的指针。这时候引发了我们的好奇继续跳转到定义

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

看到objc_object中只有一个成员 Class类型的 isa。Class又是是什么呢

typedef struct objc_class *Class;

Class其实还是一个指针类型 指向的是objc_class结构体,这时候我们继续跳转到定义 可以看到objc_class结构体的定义

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

这就相当有趣了,这个结构体中的字段似乎是在描述一个'类'的信息,比如 name('类'的名称) version ('类'的版本号) 另外还要两个重要的成员

struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists 

根据名字 ,一个是成员变量列表 一个是方法列表。有了这些信息,对于一个有面向对象编程经验的码农,这些就可以描述一个类了呀。之所以前面用单引号的'类',是我故意要做区分。还记得刚接触面向对象语言时那种不知所云的感觉么。各种书上在描述类时经常会提到的字眼就是 设计模板 设计图纸。类就使我们用代码去描述的一个抽象,它会被编译器具象为内存中的实际对象。这时候我们就会有了这么一个思考,对啊,我们平时定义类,类信息是由编译器负责维护的。一旦编译完成,内存中关于类的很多信息就消失了(当然也完全没必要存在,比如类的名字)。为什么object c要大费周章的用一个结构体的变量的去保存这些信息呢?如果是从文章一开始就跟紧"计算机科学中,任何问题都可以通过增加一层间接寻址来实现"这一思路的读者,此时应该一眼洞穿apple的用意。动态啊,动态。每一个对象有一个指向它所属'类'的指针,这个'类'实际上是存在于内存中保存着类信息的结构体变量,不同于以往的类信息只是编译前安安静静的躺在编辑器中。有了这个'类'结构体变量,我们就真的可以为所欲为了。想想都美妙,本来一旦定义了一个类,类的声明代码在编译前一经确定,编译后就没它啥事了。现在是有了一个结构体保存这些信息,每个对象都有一个指向所属'类'的指针。我们一旦改变了结构体中的内容,是不是就改变了对象的'类'呢。结构体中有方法列表,我们给这个列表添加一个表象,是不是就等于为对象的'类'动态(因为这一切都是运行时能够进行的)的添加了一个方法呢。
现在我们就对了oc的动态性有了直观的感受,有了'类'结构体。我们看到的以下代码

NSUInteger lenght = [str length];

就不再是普通的函数调用了, 我们称之为消息发送。str 是这个消息的接受者。实际上以上的代码会被编译为

objc_msgSend(str, @selector(length));

这里,利用runtime为我们提供的 objc_msgSend函数,我们向str发送了一条消息,通过str中的isa指针找到'类'结构体,在结构体的方法列表中寻找到相应的函数实现,然后调用。事实上,如果该'类'的方法列表中没有搜寻到,还会继续的向父'类'去搜寻,请大家注意到Class _Nullable super_class OBJC2_UNAVAILABLE ,super_class 这个成员变量 它也是指向'类'结构体的指针,它指向的是当前对象所属'类'的父'类' 。总之消息就这样在这些继承关系的'类'中传递,直到找到合适的处理方法,或者最终也没有找到,跑出异常。其实有了将类的信息储存于内存中,实现了一层间接寻址的概念,其他的都是水到渠成的。文章并未提到元类的概念,请读者自行搜索别的文章读,有了这篇文章做铺垫,读起来也定会很轻松。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容

  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,554评论 33 466
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,709评论 0 9
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,516评论 1 51
  • 某天在微博上看到一位叫吴红真的作者,作品全是针管画,还公布了过程图。惊叹其心思细腻!受此启发,也想着画上一幅,看看...
    云影拂尘阅读 2,651评论 18 26
  • 201705 【月度检视】 标题:2017-05【30天月度检视】姓名( #基本情况#(写孩子的)姓名:辰 年龄:...
    奔跑的郑同学阅读 505评论 0 0