Archive for 2009年11月

Wikipedia Thanks to Me[2]

2009/11/30

给 Wikipedia 捐款还有另外一个源渊。一年半以前完全转向 Mac 的一个重要原因也是因为 Wikipedia 。当时我对技术比较沮丧,每天都沉浸于阅读二战史。不过刨根问底的深度优先式阅读习惯没有改变,遇到不懂的名词都要上 Wikipedia 查查 —— 和技术文章相比,阅读二战史还需要更多的访问它。因为偶尔在公司里阅读或者手头只有 iPod touch,所以会用 Mac 或者 iPod touch 来访问 Wikipedia。结果发现它的页面在 OS X (或者 iPhone OS) 上渲染得比 Windows 上漂亮很多(后来和志岩聊起此时,尽管认为不少网站都是如此,但我们都同意 Wikipedia 的差异更明显一些)。

以当时对技术沮丧的程度,我已经认为自己不会在业余时间研究技术了。加上使用 Wikipedia 的感觉,我决定买一台给『非技术用户』使用的电脑。这算是 Wikipedia 带来的一个小小转折。当然后来 MacBook 一到手,接触到 UNIX、Cocoa、OpenGL 这些东西,对技术的感觉突然似乎又回来了。

Wikipedia Thanks to Me

2009/11/24

樊志岩要求热爱知识的人给 Wikepdia 捐款。响应号召得到了这个:

Chrome OS 的自我设限

2009/11/23

In fiction, there have been stories of laws passed forbidding the construction of “a machine in the form of the mind of man”. In fact, the competitive advantage — economic, military, even artistic — of every advance in automation is so compelling that passing laws, or having customs, that forbid such things merely assures that someone else will get them first. … … The other approach to Drexlerian confinement is to build rules into the mind of the created superhuman entity (Asimov’s Laws). I think that performance rules strict enough to be safe would also produce a device whose ability was clearly inferior to the unfettered versions (and so human competition would favor the development of the those more dangerous models).

弗诺文奇,《即将来到的技术奇异点

Chrome OS 不允许安装运行除了 OS 之外的任何 native code。有史以来,第一次电脑硬件上运行的 machine code 被限制在了一个特定的范围之内(就连 iPhone 也并未做出如此严格的限制)。Google 声称 Chrome OS 的自我设限让这个操作系统具备了空前的安全性。

谈到『自我设限』,很快想到的就是阿西莫夫设定的机器人技术的三大定律。人造的定律怎么能无法逾越?在《钢窟》中,阿西莫夫解释了三大定律坚不可摧的原因是全世界的机器人科学家心有默契,在 50 年内的发布的每一条制造机器人所需的数学定律中都嵌入了三大定律。这样,如果有人想越过三大定律,就必须独力重建全世界科学家 50 年的成果。不过,弗诺文奇在《即将到来的技术奇异点》中提出的质疑更有力。像三定律这样的自我设限必然对机器人的性能产生不利影响,在一个充满利益斗争的世界里,自我设限只能被对手利用。所以,类似『囚徒悖论』,竞争让技术必然走向无限的发展。弗诺文奇说,即使人们能意识到技术的无限发展会导致人类的灭亡,也没有办法通过自我设限来解决。

回头看看 Chrome OS 的自我设限,感觉似有相通之处。在功能方面,Chrome OS 无法创造出真正的 killer app —— 任何用于 Chrome OS 的应用都是 web 应用,所以都自动地为其它操作系统获得。从用户体验方面来说,其它的操作系统都是 Chrome OS 的不设限的超集。所以,其它操作系统上的开发者一定能利用 Chrome OS 的限制创造出比后者更好的用户体验。讽刺的是,尽管 web 版的 Lantitude 在 iPhone 上并不受限制,Google 还是对于 Apple 不让其发布 native 的 Google Lantitude for iPhone 大大的抱怨了一番;反过头来推出了一个只能运行 web 应用的操作系统。从安全方面来看,任何一个有经验的用户都能在 Linux 或者 OS X 上(甚至 Windows)达到几乎同样的安全级别。Chrome OS 除了让不重视安全的用户抱怨之外,这番苦心似乎也得白费。

更绝妙的是 Chrome OS 完全开源。由此 Google 的 vanilla 版本就处在了和其它团体的 customized 版本的竞争位置。正如弗诺文奇的解释,自我设限在功能、用户体验和安全方面的开销必然让处于竞争地位的各个版本争相放弃这些限制,从而让一个抛开自我设限这个就乏善可陈的操作系统变得毫无意义。我已经等不及一个允许允许第三方用 C 扩展 JavaScript 的 Chrome OS 修改版的出现了;下一步,允许第三方浏览器的 Chrome OS ?

顶置菜单

2009/11/11

通常,OS X 应用程序的主菜单位置是让刚刚接触 Mac 的 Windows 用户感到新奇的一个地方。主菜单不从属于任何一个窗口。所有应用程序的主菜单都在整个屏幕的最上方显示,同一时刻不可能有多于一个应用程序的菜单显示。所以 active 程序切换的时候会产生 Windows 上没有的菜单切换现象。

对于顶置主菜单与窗口主菜单的优劣,Apple 的 Bruce Tognazzini 从用户交互的效率和速度为 Mac 的风格做出了辩护。当然也有人进行了针锋相对的驳斥。在我看来,在这场围绕 Fitt’s Law(或者说指向设备 —— 即鼠标的操作速度和效率)的争论中,双方至多算打了一个平手。我自己将近三年的 Mac 开发和使用得到的感觉是:主菜单和窗口的逻辑关系要比鼠标的指向速度重要的多得多。

至少有三个原因让我认为主菜单应该和窗口分离。第一,主菜单重在一个『主』字。基本上应用程序能完成的所有功能都应该在主菜单中能够找到对应项。所以,主菜单中必然包含非针对某个窗口(窗口无关的)或者针对多个窗口的操作。最为典型的是『 Windows 』菜单 —— 列出所有打开的窗口,控制窗口的排列。让主菜单这样的 UI 元素从属于某个窗口 —— 一个后果就是每个窗口都有一个 『 Windows 』菜单 —— 从逻辑上说是十分怪异的。第二,在 93 年左右或者更早接触过电脑的人一定知道当年著名的 MDI 模式。也就是 Word 5.0 那种模式 —— 一个大的窗口包含所有的文档窗口。这似乎是 Microsoft 在不违反 Windows 本身的主菜单必须依附于窗口的前提下,尝试让主菜单脱离文档窗口的一种尝试(当然这不是 MDI 的唯一目的,但是我认为从逻辑上看这种考虑是有的)。遗憾的是,MDI 的大窗口带来的用户体验似乎是很糟糕的,让 Microsoft 不得不放弃尝试让 MDI 作为主流。随之而来的是几种不成功的尝试:Visual Basic 6.0 把主菜单做成单独的窗口(也为 Delphi 和某些版本的 Photoshop 采用);Visual Studio 把所有窗口用 dock tab 来实现(UltraEdit 类似)。前者类似 Mac 的风格,但是在处理主菜单的显示上明显没有 Mac 的置顶方式简洁,基本可以认为是 Mac 风格的胜利。而后者并非完美的方案,因为在有了 tab 的情况下用户往往仍然需要窗口级别的组织才能更好的同时处理更多文档。

第三,今天的程序往往很多功能并不体现在窗口上,所以很多程序有了一个特殊的状态 —— 没有任何窗口打开,但是程序仍然保持后台功能的运行。这个状态可以叫做 stealth 模式。MSN、Adobe Bridge、很多浏览器、播放器都有这种状态。但是如何从 stealth 模式恢复成普通模式?当主菜单依附于窗口的时候,进入了没有窗口的 stealth 模式时主菜单对于返回普通模式就无能为力了。在 Windows 下,开发者必须编写支持托盘图标(Tray Icon)的代码来支持 stealth mode。而在 Mac 上,几乎任何程序都天然的拥有 stealth 模式,无需一行特别代码来实现 —— 没有编写的代码就是 bug 最少的代码,也是最快的代码。Mac 程序在 stealth 模式下仍然可以拥有一个全功能的菜单(当然不合理的菜单项会 disable),而不是托盘图标提供的一个粗陋的菜单。Mac 的菜单切换也变成了在 stealth 模式下更好的隐藏程序从而不影响用户使用其它应用的一个优势,这是前面说的那种 Visual Basic 风格的独立窗口主菜单不具备的。

而且,今天的应用程序更多的把主菜单作为一种完备性的体现而不是用户体验的主要组成部分。快捷键,工具条,拖放,图标,右键菜单(context menu),乃至于 multi-touch pad 的 gesture;这些才是用户体验的集中体现。所以,主菜单的设置合理更多的在于逻辑关系的正确和清晰,主菜单是用户发现功能的场所,而不是使用功能的主要场所。不是 Fitt’s Law 体现的速度和效率的主要舞台。所以,即使在 Fitt’s Law 方面各执一词,在菜单切换现象上略有劣势,我仍然有理由认为 Mac 的菜单风格胜出。

Universal Binary

2009/11/09

最近 Ryan Gordon 终止了在 Linux 上实现 Fat Binary 技术的努力。这让我和甫鸼展开了一场对 Universal Binary 技术的讨论。

Universal Binary 的鼓吹者一般认为在连接器和操作系统底层直接加入对多 CPU 架构的支持( multi-arch support )可以大大简化开发者的负担和提高最终用户感受到的易用性。反对者则可以反驳说把能放在 build 脚本和 installer 中的 policy 不必要地塞到底层违反了 Unix 的原则。

抵触最大的恐怕是 Open Source 社区。毕竟这次 Ryan Gordon 放弃的直接原因就是 Linux 社区对其努力的冷落。Open Source 的发布方式可以戏称为 Universal Source —— 程序的大部分由 CPU 架构无关的源代码构成,少部分 CPU 构架相关的代码由条件编译构成或者由 build 脚本根据执行 build 的平台条件来引用不同的源文件。经由 Open Source 的安装三部曲(configure-make-make install),就能安装好一个适合目标硬件的精炼的无冗余的程序。这样的发布是完美的 —— 除了不能兼容非 Open Source 的商业模式。

要应对多 CPU 架构,就必需在程序的发布或者运行中实现应对这种架构的智能。Open Source 模式的优势在于它的发布形式能够完整的体现开发者在编写代码时就考虑到的一切应对 multi-arch 的智能,然后在安装过程中(也就是 build 过程中)由编译器、连接器和 build 脚本的强大智能来完成部署。非 Open Source 的发布模式则必须另辟蹊径。在所有面对 multi-arch 这个难题的企业中,Apple 当年的问题最为紧迫。由于要完成从 Power PC 到 Intel x86 的迁移,Apple 必须在一个时期之内能给开发者和用户一个足够简单(甚至傻瓜化)的解决方案。尽管如 Linux 社区大力主张把 multi-arch 的智能放到 build 脚本和 installer(Linux 的 package manager)为正途,而 Mac OS X 在很多方面也确实遵循 Unix 的文化,最终 Apple 还是选择了 Universal Binary 。因为我每日都在 OS X 上开发,已经习惯 Universal Binary,到了有所偏爱的程度。

为了比较各种方案的差异,设定一个例子。有一个工程,在单一 CPU 架构的时候生成四个可执行文件,六个数据文件,一共十个文件,为其增加 multi-arch 支持。假设我们拥有一个不支持 Universal Binary 的操作系统。当然相应的编译器和连接器也就不会支持 Universal Binary。但是我们仍然假设编译器和连接器的交叉编译功能很方便,只要一个 Option 就能切换目标文件的 CPU 种类。也就是说支持不同 arch 的基础能力具备,只是没有对 Universal Binary 的支持。那么,为了服务使用不同 CPU 架构的用户,我们就必须面临几种选择。第一是我们使用一套标准的工程文件,针对需要支持的每种 CPU 架构,用相应的编译器 Option 编译一次。假设有三个 CPU 构架。因为是采用完全相同的工程文件,仅仅是一个编译目标的 Option 不同,所以我们会生成 30 个文件(三个版本,每个版本十个文件)。使用不同 CPU 的用户可以选择不同的版本安装。这样的方案,build 过程中对六个数据文件(假设是平台无关的格式)的生成就是冗余的。对于拥有多于一种 CPU 架构的机器的用户来说,需要下载相应的多个版本,所以重复下载了六个数据文件(重复的次数由用户拥有的 CPU 种类决定)。

另一种选择是我们写一套特殊的工程文件。一次生成支持三个 CPU 的 12 个可执行文件(每个架构四个),而对于平台无关的六个数据文件只生成一套。所有的结果都打入一个 package。相应的,installer(或者说 unpackager)必须针对用户安装时的平台提取正确的可执行文件版本和数据文件。这种方案的缺点是必须自己在 build 脚本和 installer 里编写处理 multi-arch 的智能。Universal Binary 的反对者会说这些智能并不复杂,Build 脚本和 installer 正是实现它们的好地方。但是,实际上每个工程实现这些智能的方式都会有些微的区别。这就意味着,用户从一个产品得到的知识并不能用到另一个产品里 —— 比如检查该产品支持的所有架构,如果是 Universal Binary 的话可以用统一的命令查询。开发者从一个工程转向另一个工程也必须通过文档或者别人的描述才能熟悉这个工程特有的 multi-arch 处理。

对于上述的工程例子,支持 Universal Binary 的系统则是一次生成十个文件,只在四个可执行文件中同时包含三种 CPU 架构的代码(用 ELF 的术语来说是三个 section )。这种方式为反对者诟病之处在于,安装好的系统存在冗余 —— 磁盘上会有用不到的机器码,提高了连接器和操作系统的复杂度,违反了 Unix 的 Mechanism, not policy 的原则。不过,任何稍稍违背这一原则的做法都会带来一个好处 —— 标准化带来的统一性。在《C++与垃圾回收》一文中我曾经说过,正是因为 C++ 把各种高级内存管理策略放到了库而不是语言级别,才让这些高级策略不能得到强制统一而形同虚设。所以,mechanism-not-policy 需要灵活的「道」来处理而不能一味的极端化。

Universal Binary 其实可以看作一种次优的方案。它的冗余度没有达到最优(与定制编写 build 脚本和 instaler 相比),但是大大低于第一种方案。即使存在冗余度,冗余的粒度也很小,属于次文件级别(ELF 格式为例,即 section 级别)。而且这个冗余度也带来了一些智能 installer 方案不具备的好处,就是安装之后的程序可以在不同架构的 CPU 的机器之间自由拷贝(前提是程序具备这种在任意位置运行的能力,而 Mac OS X 的 Single-File Application 恰恰是这种风格)。Universal Binary 还是一个非强制性使用的功能。连接器和操作系统只是提供支持 multi-arch 的能力,选择打入哪些 CPU 架构的执行码完全由开发者自己决定。从这个角度来说,Universal Binary 并不是真的违背 mechanism-not-policy 的原则。Universal Binary 产生于 Mac 的特殊历史,也与其文化相辅相成 —— Single-File Application,也就是 installer 完全零智能;无中央化的包管理(通过 Guide 来保证用户体验的一致);相当数量的用户(由于 transition 阶段的存在)拥有或者在一段时间拥有基于不同 CPU 架构的多台机器,需要程序能够在机器之间自由拷贝并保持工作(由于 Single-File Application 的文化)。

Universal Binary 是一个能提供有别其它方案的独特优势的方案,当然也引入了自身的代价。综合考虑,这些代价在 Mac 特有的文化中是完全可以接受的,也就是说 Universal Binary 在 Mac 的发展中是最好的选择。至于这种收益代价的比例在其它操作系统上如何还有待考验。不过我认为其它系统应该保持一种开放的态度。