Archive for 2010年11月

开发者的厌恶 —— Objective-C/Cocoa

2010/11/23

前几天看 Buzz 有感匆就了一篇超短的《开发者的厌恶》,涉及了一些很有趣的问题:一个平台,应该如何在开发者和用户之间达到平衡。作为程序员,我不会不知道一个受开发者喜爱的平台才能具有生命力,《 The Art of UNIX Programming 》里大书特书的 programming barrier 、 hobbyist programming 和 fun to hack 等概念是我经常引用的。但另一方面,以纯用户身份转向 Mac 进而成为其开发者的经历 [1],以及 Mac 成功的事实,也说明它们之间没有绝对的优先级,必须在两个相反方向上达到平衡。

达到平衡需要顾及的问题很多。先谈谈 Objective-C 和 Cocoa 这个方面。我对 Objective-C 和 Cocoa 的看法是:远非完美,但是还没有更好的替代。

如果谈及 Objective-C/Cocoa 的不可替代性,就必须谈及 C++ 。业界普及 C++ 已经有几十年,有了 MFC 、QT 等等 framework 。如果说我们认为这些东西是够用的,那么大谈 Objective-C/Cocoa 如何不可替代就是无稽之谈。在这个 blog 里我专门用一个类别来讨论 C++ 的问题。简而言之,我认为业界必须逐渐脱离 C++ 的绑架(如果有人说 GoF 绑架了什么,那么程度不及 C++ 百分之一)。如果你还认为 C++ 足以堪用,那么可以略过这篇 blog。

第二,如果谈及 C++ 的缺点,就必须找替代物。否则一个远非完美但不可替代的 C++ 和一个远非完美但不可替代的某某语言没有比较的必要。对系统开发来说替代物显而易见,Linux kernel 和 git 的成功都说明我们需要的代码组织能力即使是原始的 C 也可以提供。问题在于用户界面的开发,即便轻视面向的人也大都认为界面开发必须使用面向对象。由于其复杂性,面向对象和设计模式是编程语言在这个领域的自然而且必要的延展。所以,必须找到脱离 C++ 的面向对象方案。

这个方案必须具备统一的内存管理。当我说内存管理,我的底线是『线程安全的引用计数』。其实 OS X 的 Objective-C 2.0 已经提供了(可选的)基于引用跟踪的垃圾回收(tracing GC) 。但是我仍然要强调引用计数是一个稳健而明智的方案。关于这一点我在《 C++ 与垃圾回收》中讨论过,针对实际工程中的内存问题,泄漏其实是个无伤大雅的问题,而且 tracing GC 解决内存泄漏也远非完美,内存管理更多的是要解决生命周期,尤其是生命周期过早结束的问题,这点来说引用技术很成功;C++ boost 的 shared_ptr 堪称完美,可惜作为库而非语言组成其强制力大打折扣;Cocoa 的引用计数方案相对来说具备了语言级别的强制力,虽然不是基于栈和作用域的自动方案,但是借助方法的命名规范和 Xcode 的代码静态分析,基本上能达到相同的效果。

当我说最需要统一的内存管理解决的问题是生命周期问题,我指的是普遍的在基于消息循环和派发的环境中的生命周期问题而非狭义的对象生命周期。所以,面向对象需要内存管理方案,而要对整个 code base 和整个开发团队施行统一的内存管理方案,也需要一种面向对象语言,它们在界面开发中是互相依赖的。

一个疑问是这个方案是否必须基于非本地码(也就是 byte code 或 P-code 等非 native code)的虚拟机。我倾向于否定。首先是 OS X 出现的时机,在 2005 年以前,我不认为开发者有信心在当时的硬件条件上使用虚拟机方案。当然,今天是 2010 年。不过,我一向认为好的产品是 do more with more,而不是 do acceptable with less。好的程序员会把 native code 带来的性能变成对产品有利的东西。在那些提供所谓简化模型的虚拟机平台上提供和 native code 具有同样竞争力的界面功能经常需要降低代码的可读性。不可否认,在更原始的平台上开发一个功能需要自己编写更多的底层函数。但是有两个补偿,第一是你有很多 open source 库,不用等待或者自己把它们移植到 Java 或者 ActionScript 上;第二,如果你的代码要维护很多年,你可以写更直观的代码(由于 native code 带来的性能收益),而且多维护几个函数并不会影响代码的整体和局部可读性。另一个原因是,虚拟机方案还能带来什么?一个错误的程序在虚拟机上抛出一个无法恢复的 except,同样错误的程序在 native code app 中造成崩溃并且生成详细的 callstack,甚至生成详细的 core dump。很大的差别吗?

说到差别,我们对所谓低级语言和高级语言的在软件开发中对生产力影响的差别很大程度上夸大了事实。对自动 tracing GC 和虚拟机语言的鼓吹,其实是一贯忽视代码静态检查和 crash callstack report 一类工具的结果。

如果抛开对虚拟机的痴迷,支持 Java 语言对 Mac 平台来说就少了许多吸引力。接下来,Java 在各个层面都有不能容忍的问题。首先,Oracle 对 Google 的诉讼再一次告诉我们,Apple 不可能使用一个由另一家公司完全控制的语言。其次,让语言和 OS 分属不同公司控制也是开发者完成工作的噩梦,同属虚拟机方案的 WPF 用户体验尚不算寒酸。而 Java 无论在哪个不受 Sun 控制的平台上的用户体验都有目共睹(其实我用加那个定语吗)。

接下来,Objective-C 的语法。如果你真的看过我上面的描述,如果 Objective-C 在上面这些方面做得比其它所有语言都稍稍好一些,那么它的语法就算比今天还糟糕又如何?Not a big deal !难道,Java 里用匿名类安装回调函数的语法非常优美?难道 C++ template 和 operator overload 的词句十分押韵?别,连美国人都开始说 long time no see 了。

Apple 应该在 2000 年的时候创建另一种语法完美的语言吗?(或者说 NeXT 在更早些时候?)我不知道。也许如果那发生了,今天我们就会皆大欢喜。可惜的是没有,就像今天的汉字不都是形声字能让我们认半边。我不想讨论 Apple(或者 NeXT )在当时花精力构建一种新语言是在构建完美未来还是在自身难保、资源捉襟见肘的形势下自寻死路,只想说从构建更好的软件出发,如果今天完全剔除 Objective-C/Cocoa 所有引起开发者厌恶的东西是得不偿失的无的放矢。

注释:

  1. 我从 07 年开始在 Mac 上写 code 。不过完全是工作所需。这里的作为用户转向 Mac 和成为其开发者指的是我在自由时间的选择。

开发者的厌恶

2010/11/22

Mike Lee 上周讨论 Mac OS X 为什么容易遭到 geek 冷淡的时候说,Apple 经常以它自己对用户体验的理解为依据禁止开发者做特定的事情(或者让用特定的方式以外的方式变得异常困难),但是他又说一个不能对任何人说『不』的系统(指的是 Android)也并不能讨好大多数人。

最近用 Cocoa 开发一个小程序,能体会到作为开发者很难不被这个平台的很多细节搞得精疲力尽乃至产生厌恶。但是,喜欢上 OS X 的时候我不是一个开发者(当然我还要写代码来工作赚钱,但是当时已经不再把开发作为兴趣),但是喜欢上 OS X 之后却情不自禁地产生为它开发程序的兴趣。能持续发展的平台应该把开发者作为用户吸引过来。那些声称能让开发者『集中精力在主要问题上』的平台也许忘记了开发者关心的主要问题并非用户所想(即使是那些声称重视用户需求的开发者)。

程序猿

2010/11/20

最近一直没有写 blog。时间都被其它事情占去了。第一件大事是在等待三年之后终于开始摆弄这个星球上最好的 code base 之一。第二件事是又开始写一个 hobbyist 应用,再次让我感受到能体会软件开发脉动的唯一方法就是多写 code。