Objective-C runtime学习 - 上 - 瞎逼逼

wbiblem 2019-07-01

前言

入坑iOS开发已经一年半,对所学所用的知识却一直没有好好做梳理,这里梳理一下我对Objective-C runtime的学习和理解。

runtime是OC这个语言的核心,想要弄明白runtime,就不得不从语言设计层面开始说起,本篇主要讲讲这些宏观的东西,下篇再关注其实现细节与应用。

正文

20世纪80年代,大家逐渐意识到在复杂系统开发和GUI编程中,面向对象编程有巨大的价值。
由于当时C语言的广泛应用,一个比较直接的想法就是,在C语言的基础上扩展出面向对象能力,能够最大程度上减少开发者的迁移成本。
于是Objective-C和C++都在这段时间里诞生了。

不过这两种语言在实现上都有着巨大的区别。
Objective-C选择使用C语言实现一套面向对象的库(我们称之为Objective-C的runtime),并给Objective-C添加了一些面向对象的语法。编译时,Objective-C的语法被处理为C的语法,然后和runtime库放到一起编译。因此,Objective-C可以基于runtime实现一些动态特性,我的程序里打包了一个运行时系统嘛。
而C++选择完全在编译期实现其面向对象特性。

这两种截然不同的实现,反映了设计者对语言动态特性的不同考量。其中最重要的一点是,对“面向对象”这一编程范式的理念不同。

面向对象思想意味着把程序抽象为各种独立而又互相调用的对象。在语言设计上,这里需要面对一个具体的问题,对象间的通信,应当是种什么形式?
直觉上,对象间的通信,直接用方法调用的形式就好了。

世界上第一个面向对象的语言Simula就是这么实现的。但实际上会面临一些问题。具体来讲,当你调用一个对象的一个方法时,你怎么知道它有这个方法呢?这意味着你要确定这个对象的类型,也就是说,你必须先知道一个对象的类型才能对它进行调用。

在很多场景下,这种限制是不必要的。于是,世界上第二个面向对象的语言Smalltalk进行了一些改进,它实现了一套与目标对象无关的消息传递机制。消息的发送方可以不关注接收方的类型,也不关注接收方是否可以正常处理这条消息。而消息的接收方需要对收到的消息做处理,有对应的方法实现就去调用,没有就走异常处理流程。

C++传承了Simula的“静态消息机制”,而Objective-C传承了Smalltalk的“动态消息机制”。这两者的区别看似没那么大,却在很大程度上影响了之后面向对象体系的发展。

回到我们的Objective-C,为了实现这种“动态消息机制”,通常需要一个运行时系统来处理。Objective-C的发明者选择了最简单有效的方式,实现一套简单的运行时系统跟程序打包到一起。比起后来Java的虚拟机或JavaScript的执行引擎,Objective-C的实现充分利用了原有的C编译器,只要给Objective-C做个预处理器,把Objective-C代码转成C代码然后和runtime库放一起用C的编译器编译就可以了,实现难度和工作量都是比较低的。此外,这样的实现保证了Objective-C跟C的完全兼容,可以说,Objective-C代码在编译时就是C代码,因此可以和C或C++混编。

结语

总体而言,Objective-C的语言设计至少在当时还是比较优越的。“动态消息机制”现在看来还是带来了一些好处的。
而在C的基础上添加一个很薄的runtime层来为C提供面向对象机制,也是个很合适的做法。

后记

想要从宏观上理解清楚Objective-C runtime的来龙去脉,其实需要对整个面向对象的发展史有足够的理解,对面向对象的各种实现的优缺点有一定认知,显然我的能力是远远不够的。本文也只能简单梳理我个人的一点理解,可能还有颇多错漏。
另外,Objective-C的语言设计在当时有着不少优点的,但是Objective-C的语言发展已经停滞不前多年,比起一些热门的语言它在一定程度上是落后不少的,而苹果已经拥抱swift,因此这种停滞可能是永久性的。


扩展阅读

  1. function/bind的救赎(上)——孟岩
  2. C++多继承有什么坏处,Java的接口为什么可以摈弃这些坏处? - invalid s的回答 - 知乎

相关推荐