文 / 唐天勇 LeanCloud 工程师
Objective-C 是开发 OS X 和 iOS 应用的标准语言。即便是天天跟它打交道的开发者,有些也会误以为 Objective-C 就是 Apple 公司创建出来的语言,但实际上它并不是 Apple 的亲骨肉,而是从别人家过继过来的孩子。
程序设计语言是一个规范,它可以有许多种实现。在历史的漫漫长河中也出现过其他 Objective-C 实现,下面我会主要以 Apple 的 Objective-C 实现来论述。
出生
Objective-C 的诞生要追溯到 1980 年左右。那时 Brad Cox 和 Tom Love 两位工程师还在 ITT 实验室工作,他们意识到程序设计语言的抽象程度在软件开发中扮演着重要角色。他们认为 Smalltalk 在这方面做得非常出色,但执行效率是瓶颈,并且实验室里大部分系统软件都是用 C 语言实现的,因此他们希望把 Smalltalk 的好处带到 C 语言里。Objective-C 就是在这样的背景下诞生了。
成长
1983 年,Brad Cox 和 Tom Love 成立了 Stepstone 公司,并发布了第一个 Objective-C 实现。据 Brad Cox 回忆,最初的实现就是个简单的预处理器。他们使用 sed 和 awk 等工具把 Objective-C 直接翻译为 C 语言。可 C 语言并不原生支持 Objective-C 的动态语义,例如 dynamic dispatch。动态语义是通过一个 library 来实现的,这个 library 逐渐发展成了现在的 runtime library(简称 runtime)。
后来 Stepstone 被 Steve Jobs 领导的 NeXT 收购,Objective-C 由此迎来它的首次发展。不过 NeXT 并没有在语言中引入新特性,而是对 runtime 做了一些优化,即 NeXT runtime。
Apple 收购了老乔的 NeXT 后,就把 NeXT 当时在使用的 Objective-C 直接继承了过来,即所谓的「Objective-C 1.0」。后来 Apple 在语言中增加了一些新特性,例如属性、fast enumeration、垃圾回收(后来被 ARC 取代)。不久前 Apple 为Objective-C 引入了轻量级泛型。这样的改进可谓进步巨大,runtime 也被完全重写并开源了出来。 Apple 将这一新的实现称为「Objective-C 2.0」,也就是我们现在看到的这个样子。
Apple 对语言的持续改进让开发者欢欣鼓舞。Objective-C 2.0 也在 OS X 和 iOS 应用开发的浪潮中站稳了脚跟。作为语言的使用者,我对 Objective-C 语言是爱恨交加。一方面,小巧的语言结构、动态的本质让开发变得多姿多彩,特有的方括号也让代码添了几分味道;另一方面,从现代的眼光来看,语言缺乏一些高级特征,比如命名空间 namespace、完整的泛型支持等。虽然仍不完美,但承担起 Apple 应用开发的重担,它做到了。
性格
早期的 Objective-C 只支持手动内存管理,开发者必须仔细去跟踪每个对象的生命周期,犹如做针线活一般,步步小心谨慎,否则就会扎破手指。而同时期的许多语言都已开始引入了更高级的内存管理,例如垃圾回收,Java 就是其中的典型代表。
虽然手动内存管理在执行效率上有优势,但同时也带来了额外的心智负担。好在后来引入了自动引用计数 ARC。虽然它的背后仍然是引用计数,但大多数时候不需要开发者跟踪对象的生命周期,而是交由编译器和 runtime 来做。开发者只需要注意一些特殊情况即可,例如循环引用。
Objective-C 不支持 namespace,也就意味着它不支持嵌套类,所有符号会在编译阶段连接到一个全局的 namespace 下。如果连接时发现了重复的符号,编译就会失败。为何 Apple 迟迟不在 Objective-C 中引入 namespace 呢?
其实 Apple 曾多次考虑过这个问题,但发现这是个大坑,填起来困难重重。困难之一是与 C 和 C++ 之间的协调。由于 Apple 允许 Objective-C 和 C++ 混合编程(Objective-C++),这就要求 Objective-C 的 namespace 与 C++ 的保持兼容,然而这是不可能的。
另一种选择是只对 Objective-C 语言实现 namespace,不过也有问题。因为 Objective-C 语言是 C 语言的超集,C 语言没有 namespace 的概念,其结果只能对 Objective-C 的类实现 namespace。但是 Objective-C 中的那些方法又会最终编译成 C 语言的函数,只能对方法做 name mangling,这便会造成兼容性的问题。目前 Apple 尚未找到能完美解决这一问题的方案,Objective-C 也只能先感慨着与 namespace 的缘分未到了。</p>
Objective-C 早期不支持匿名函数或者闭包,但随着社区的呼声越来越高,Apple 最终为 Objective-C 增加了这一特性。不过有趣的是, Apple 是直接在 C 语言中实现的,称之为 block。Objective-C 扩展了 block,以适应 Objective-C 的内存管理。
Objective-C 1.0 不支持泛型。直到 Objective-C 2.0 才引入了轻量级泛型。注意,之所以轻量级是因为它完全由编译器实现,没有 runtime 参与,泛型信息在代码生成阶段就被丢弃了。可能 Apple 也不希望在 runtime 中引入额外的代价吧。
社区
随着 OS X 和 iOS 开发生态的繁荣。越来越多的开发者投入到 OS X 和 iOS 应用开发中来,社区涌现出许多优秀的第三方库。
第三方库不断积累,包依赖管理亟待解决。可 Apple 并没有官方的解决方案。既然没有官方的,那就自己动手做一个吧,CocoaPods 就是这样问世的。不过由于 Xcode 的封闭,CocoaPods 的开发过程也非常艰难,经常因 Xcode 的升级而出现不兼容的状况。但 CocoaPods 最终克服了重重障碍,成为管理依赖的首选。
社区的不断壮大让 Objective-C 长期占据着 TIOBE 排行榜 前 10 的位子,所以这门古老的语言在 20 世纪依然能绽放耀眼的光芒。
未来
2014 年一声炮响,Swift 横空出世,Apple 不遗余力地向世界宣告着这个亲骨肉的到来,而有关继子 Objective-C 该何去何从的讨论也愈演愈烈。
诚然,Swift 解决了 Objective-C 的许多痛点,例如支持 namespace、运算符重载、更轻松的内存管理等。我想,Objective-C 不会在短时间内离我们远去,因为社区中还有大量的 codebase 是用 Objective-C 写的。并且,Objective-C 与 C / C++ 语言更亲近,如果项目中需要和大量 C / C++ 交互,Objective-C 仍然会继续发挥余热。
关于 Swift 的那些事儿,我会在下一篇文章中继续聊。感谢阅读!
请发表评论