天天看点

从Windows角度看Mac OS X上的软件开发

    如果原来从事windows软件开发,想跨足或转换至mac os x环境,需要知道那些东西?有什么知识技能可以快速运用在mac os x环境上的?这两个问题应该是windows开发者进入mac os x环境最关心的问题。本文假设读者以往采用微软的开发工具,并以c/c++/c#的任一种组合作为开发语言。

大体说来,windows和mac os x都是为桌面应用环境、图形用户接口(gui)而设计的操作系统。虽然不同平台细节各有特色,但两者相近的抽象概念,其实远远多于相左之处。本文试图指出方向上明显的异同所在,而非详细列举各种细项差别。最后,我也将简短分享自己在开发跨平台软件时的一些技巧和心得。

系统架构与开发环境的差异 

用最简单的话来说,mac os x与windows在架构与开发环境上最大的不同点在于:os x是unix也不是unix;os x主要开发工具xcode使用gcc作为编译程序,与其他种类的unix相同;不过os x也有独树一格的"bundle"软件包装格式这样的东西,成为它与其他操作系统不同之处。

windows和os x都属于现代的操作系统,所以windows在操作系统层级所提供的功能──执行文件与链接库加载、多任务与多线程、内存管理──在os x上都找得到对等的api和作法。不过,相较于windows在微软独力开发下,架构和api都维持着相对的一贯性(另一方面,也背负着各种历史遗迹和向下相容的包袱),mac os x则是底层源自nextstep的mach微核心(现在称为xnu),而应用层(用准确的unix术语来说叫userland)来自freebsd 4。这件事情相当重要:os x透过这样的架构,才拥有和一般linux/freebsd相似的unix应用环境。有相当多mac软件开发者喜欢在unix

shell下工作,使用各种unix工具。在windows上,必须加装cygwin之类的环境才能办到。

x受益自unix环境之处不少。尤其,apple使用了大量的open source工具。举例来说,apple不像微软,没有自己的c语言编译工具,apple用的是unix业界的标准──open source的gcc(其中当然有不少os x的扩展功能就是)。虽然apple有自己的开发环境xcode,但是底层采用gcc这件事对开发者来说是相当重要的。同时,apple的c/c++链接库用的也是gcc标准的stdc/stdc++。了解这个差异,在遇到与microsoft c/c++ compiler不同的地方时,就更容易能找到解答的资源(这类型问题往往不限于os

x,其他unix平台也会发现)。

但是mac os x并不完全是unix。它的gui环境(aqua)就完全不是一般linux/freebsd所使用的x11。而在unix层之下的微核心也和其他unix不同。接下来这一点很重要:os x虽然有和windows .exe和.dll相对应的文件(os x跟其他unix一样,可执行文件一般不加扩展名,unix系的动态加载链接库则冠以.dylib),但更重要的架构差异是bundle。

bundle概念承袭自nextstep。简单来说,就是由操作系统提供一种类似对象封装的文件包裹。os x上最常见的bundle要属.app结尾的应用程序了。虽然.app外观上是个文件,在unix shell下看就能发现它其实是个目录,内含各种metadata(通常至少会有一个名为info.plist的数据文件)、可执行文件、动态链接模块、各种资源等。除了.app外,os x的各种框架档(以.framework结尾,是一种同时包含头文件及链接库的包装)、应用程序的外挂模块(通常以.bundle结尾)等等,都是以bundle形式呈现的。了解这个差异,才能了解为什么os

x上很少有程序需要额外的安装程序,也鲜少听说有所谓的"dll hell"(因共享链接库版本不兼容造成的困扰)。

项目 windows mac os x

操作系统最近桌面版本 windows vista mac os x 10.5 leopard

操作系统核心 nt kernel xnu

cli shell环境 cmd.exe unix shell (bash/tcsh/etc., 可使用terminal.app一类的终端机软件进入)

gui (shell) 环境 windows explorer aqua (finder)

程序二进制文件格式 portable executable (pe): .exe, .dll mach-o "universal" binary (可执行文件通常不带附加名,dll结尾为.dylib)

用来辨认软件组件的方式 guid bundle identifier (java式的id,例如com.apple.textedit)

厂商提供或贩卖的开发环境 microsoft visual studio xcode

可视化的gui制作工具 visual studio内建的winform designer interface builder

c编译程序 microsoft c compiler gcc

表一:windows与mac os x在架构上的对照

开发语言与api;objecitve-c, core api, carbon, cocoa 

如果使用微软工具来开发windows软件,就一定会碰到platform sdk,mfc或者.net平台,同时,也相对应到c、c++、c#和其他.net平台所提供的语言(这种区分并不是绝对的,仅仅是为了方便接下来的模拟所做的简化)。在os x上,apple则是鼓励大家尽量采用objective-c作为开发语言,并且熟悉cocoa。

接下来的问题既尴尬又麻烦。很多人会问:我们是否非学objective-c不可?另外一个常见的问题是:apple不是也有名叫carbon的c api吗?(延伸出来的问题则是:可不可以用c++开发mac程序?)。

简单的答案(同时一定程度上也代表apple的态度)是:要用objective-c才能完全发挥os x图形应用环境的长处,而cocoa这个用objective-c写成的api framework就是最佳的施力点。

复杂的答案则是这样:

os x的本体,也就是所有非unix的部份,并不像windows一开始就(几乎)全以c写成的。因此os x没有所谓"win32 api"这么纯粹的东西。os x核心的、非gui的服务和链接库,有时称为"core api"。core api大部分以c写成,并且多半奠基于corefoundation这套链接库之上。corefoundation提供了一贯的内存管理模式(cfretain, cfrelease)、基础的数据型别(字符串、数组、字典)、property list文件管理、文件、网络存取等等。corefoundation使用上跟win32

api有点相似,都透过存取handle的方式来达到某种近似「用c语言操作对象」的效果。但corefoundation最大的不同在于它还有reference counting的内存管理模式,大幅简化了内存管理的复杂性。

至于carbon,严格说来,是mac os x在发行之初,为了维持与mac os 9兼容,才提供一套以c写成的gui工具集,主要包括所有的gui组件(apple 称为 hitoolbox ,hi 意思是 human interface)以及所有os x之前的api(quickdraw等等)。随着os x 10.5的推出,apple渐渐舍弃了旧式的api ,鼓励大家使用objective-c写成的cocoa来开发程序。carbon现在的意义等于就是hitoolbox,也就是os x gui 的c api。

但是,apple在2007年夏天做了重大的宣布;carbon不会有64-bit的版本。也就是说这一套c api是「没有未来」的。这意味着所有使用carbon写成的软件──microsoft office、adobe photoshop都不可能顺利过渡到64-bit。至于像qt这一类跨平台的gui kit也势必要顺应这项改变。

其实objective-c并不难学。由c转换到c++/c#时需要学习很多新观念、新用语,但objective-c大体上只是在c语言上加上一层薄薄的、动态的面向对象层。cocoa则是相当容易上手的api。透过cocoa就可以用面向对象的方式存取os x八成上的系统服务(其余两成可以用c来呼叫)。objective-c可以跟c完全混用。同时apple也提供了所谓的"objective-c++",可以在c++程序中呼叫objective-c程序,或者在objective-c里撰写c++程序代码。apple自家的浏览器safari就有不少核心的程序代码(webkit)使用了objective-c++来撰写。 

主要开发语言 c/c++/c#(及其他.net支持的语言,如c++/cli) objective-c/objective-c++/c/c++

操作系统服务 win32 api 系统服务多半可从posix layer用stdc/stdc++取用

系统核心服务 win32 api corefoundation/coreservices

绘图与gui win32 (gdi32, user32) quartz (c api)/hitoolbox (carbon)/appkit (cocoa)

面向对象的api .net framework/mfc cocoa

面向对象的gui及绘图系统 wpf/gdi (with mfc) appkit (cocoa)以及cocoa graphics

桌面应用程序的数据库方案 odbc/ado.net coredata

基础绘图系统使用的单位 pixel (gdi) point (quartz)

默认的屏幕分辨率 96 dpi 72 dpi

表二:开发语言与api的对照

图形作业环境的差异:绘图系统 

大家对os x最主要的印象,想必还是它的图形作业环境。gui的确是os x与windows差异最多的地方。

在windows环境里,传统上win32 api同时包括了绘图(所谓的gdi/gdi+)和gui组件(窗口、对话盒、按钮等等)的操作。到了.net 3.0有所谓的wpf (windows presentation foundation)。严格说来所有windows上的概念和组件,都可以在os x上找到相对应的作法。但是在架构上os x确实和windows有相当大的差异。

os x的绘图系统核心是quartz。quartz的绘图基础概念是路径(path),而不是像素(pixel)。惊人的事实是:quartz是一套pdf绘图系统。所有quartz能绘制的对象都能轻易转换为pdf文件。至于在图像处理上,quartz提供了一套完整的合成模型(compositing model)。简单地说,quartz赋予了mac os x极为优异的绘图能力。从一些细节就可以看出quartz在视觉上的细致度:例如,os x在显示字型时的去锯齿(anti-aliasing)处理就要比windows来得细腻,在点阵影像的缩放上效果也往往比windows好。os

x的应用程序可以轻易做出各种透明度的图层、以及为图形对象加上阴影、或者绘制不规则形状(但这并不代表你应该只是为了为了吹嘘而滥用这些功能,我们马上会提到用户体验这件事)。倒是有个细节应该马上一提,那就是quartz的默认分辨率是72 dpi,所使用的单位是点(point),这跟pdf绘图系统是一致的,和windows预设为96 dpi、以像素为单位的点阵式绘图系统很不一样。这在一开始可能很困扰人。因为在os x上,不改变屏幕设定的情况下,12 pt的字,就真的会被会绘制成12 px(而在windows上,12

pt却是16 px)。同时,quartz默认的坐标系统跟数学上的习惯相同,也就是(0, 0)坐标起点是位于左下方,而不是一般计算机绘图使用的左上方(当然,quartz有各种坐标变换功能,因此当然还是可以把(0, 0)设定为左上方的)。

看似复杂,然而,当你开始想输出pdf(打印作业大幅简化)或进行精细的绘图工作时,慢慢就会发现quartz这样设计的直观了。

另外,quartz的基础api是以c写成的,所有对象操作方式都跟corefoundation一样(从quartz建立的对象都是用reference counting的方式在管理内存,同时也都可以用cfrelease来释放)。不过,cocoa也提供了绝大多数的api对应。使用objective-c来操作绘图对象会更轻松些。

在quartz之上,或者与quartz并行的,还有apple的各种图形和媒体相关的子系统。诸如可以快速制作动画的quartz composer、新一代文字输出编排系统coretext、应用层的2d动画系统coreanimation,以及apple的招牌多媒体架构quicktime,还有业界标准的opengl,这些构成了mac os x在视觉及媒体经验上的核心。

图形作业环境的差异:gui,以及,用户体验 

windows上,尤其是win32 api里面,绝大多数关于gui的概念和技能,都可以直接转换到os x上。os x的gui同样是采用事件驱动模型(event-driven model)来设计的,每个gui应用程序同样都有所谓的run loop(或称event loop/message loop)。两者甚至在某些系统限制上也雷同:例如,.net跟cocoa都不鼓励或甚至禁止程序在主线程以外的地方创建或操作gui对象。

尽管如此,gui是造就mac os x在外观上与其他平台不同的最大要素。与之相伴的是os x对于用户体验近乎执着的追求。

os x在gui上并没有一个特别的子系统。通常我们用接触到的api来区分。好比说如果用的是carbon我们会称为hitoolkit,如果用的是cocoa则会说是appkit(cocoa主要是由非gui的foundation──不要和corefoudation搞混了──以及提供gui组件的appkit所组成的)。apple的开发工具中并没有类似visual basic一类把接口画完、在组件上点两下鼠标,把程序填进去就完成应用程序的工具或流程。最接近的是interface builder (ib)这套工具。ib做出来的.nib文件其实就是封存好的gui对象,生成之后再回xcode将必要的连结关系拉完,程序代码填上(通常量不会很多)就完成程序了。ib会是xcode以外,os

x开发者最常用的工具。

os x提供的gui组件特色为细腻、一致、直观。这并不代表os x的gui无法做复杂的设定和客制化。但是相较之下,os x的应用程序更倾向于善用或组合现有的视觉元素,而较少自创新的custom control。这一点和windows上,尤其是小型工具程序,喜欢一种程序就创造一种视觉风格,或是大量提供使用者可更换的skin,有着相当大的文化差异。虽然apple自家的软件跟微软相似,喜欢提前使用下一个版本才出现的视觉风格或元素,有时让开发者觉得难以捉摸,但大体上遵守apple自家的hig (human interface

guideline)还是常态。

我们提到了文化差异;os x在视觉上的细腻,以及对用户体验的追求,造就了一种高要求的文化。这可以说是一种正向循环。我们或许很少听说哪个windows开发者会为了icon向左偏了1 pixel而大改特改,或是要求自己的软件要在视觉及操作上符合哪个规范的一致性。但os x的开发者真的会谈论并严肃看待这件事情(著名的icon设计商iconfactory以及独立软件商panic是著名的两个代表),同样的也有相当多os x使用者以同样严苛的标准看待他们使用的软件,甚至可能写信告诉你,指出你的软件在用户体验或视觉设计上的缺陷(笔者就曾经收到使用者来信,指出笔者的一个软件在pull-down

menu中使用的icon「语意」不合乎用户对该种gui组件的期待)。又好比说,从os x 10.5 leopard开始,icon最大可以大到512x512,apple也强烈建议开发者要准备这么大的尺寸(除了原有的16x16、32x32、128x128之外)。这当然无形中提高了开发的挑战。windows在xp以前仅支持16x16、32x32、48x48,直到vista才开始加大到64x64和256x256。

另一个与gui不直接相关,但却影响用户体验的,是os x的本地化(localization)系统。这一点也是和windows不同的地方。os x因为有bundle的设计,因此能让一个应用程序同时包装各种不同语系的资源文件,同时开发多语系程序在os x上也相对容易(通常是以提供各种不同版本的.nib bundle放进应用程序bundle中content/resources/底下以语系区域来区分的子目录中就完成了。windows程序设计一向以"resource file"概念来管理icon及本地化等「外部」资源,名称相似,开发方式却不那么一贯而直观;另外,os

x的语系是可以按照顺序fallback的,例如要是繁体中文语系档找不到,而用户在语言设定中将简体中文设定在繁体中文的后头,那么os x便会尝试套用简体中文语系档),结果是os x使用者对本地化同样有着高标准与高期待。另一方面,笔者也建议大家,除非软件确定只有中文用户使用,不然一开始先以英文界面开发,再加上中文的本地化资源,以长期来说是值得(甚至是必要)的投资。

一些较难归类但同样重要的差别 

mac os x跟windows在软件开发作法上的差异还有很多,上述只就最大的方向差异阐释。有些较细微但值得一提的差别,我们也在这里简单说明。

首先,os x跟windows一样,内部字符串编码以unicode为准。但在操作系统不同的层级,使用方式并不相同。windows的unicode layer很一致地使用了utf-16作为编码,并偏好使用bom辅助判别。os x的文件系统使用utf-8,而corefoundation及cocoa则用utf-16。如果使用cocoa自己的serialization机制,cocoa会正确储存和还原utf-16的位顺序。不过,笔者自己建议,尽可能使用utf-8作为各种交换时的编码(相对于windows对于utf-8的支持不够干脆简明,cocoa自己就提供了像stringwithutf8string以及utf8string两种nsstring的method,方便在native

string与utf-8间的游走)。

其次,相对于windows使用registry来管理应用程序设定,mac os x使用的是一种叫做property list(文件扩展名为.plist,简称plist)的xml文件。plist可以直接变成corefoundation及cocoa的各种容器对象,也可以将后者轻易地serialize成plist。因此os x上的应用程序大量使用plist作为配置文件的格式,甚至作为数据单元格式。将设定用个别文件储存也减少了windows集中管理registry所带来的各种弊病。

mac os x并不使用com (component object model)来作为面向对象的进程间通信(ipc; interprocess communication)的机制。因为用cocoa写成的程序,可以透过objective-c distributed object (do)这个强大机制来达成ipc的任务。除此之外,因为bundle架构,os x软件要设计外挂模块架构也相当容易。os x有相当多支持外挂的应用程序,应归功于这种开发上的便利度。

os x应用程序能够利用所有os x在unix环境上所提供的功能。同时os x一安装好就已经帮你准备好了大量的open source链接库,例如可用来制作密码密钥认证的openssl、负责解压缩的libz、内嵌式数据库引擎sqlite等等。这些都是加速开发的好帮手。

最后要提的是,正因为os x的文化与windows有许多不同处,笔者建议跨足os x的开发者应该要尽可能贴近甚至配合os x的习惯。举例来说,大多数os x应用程序都不需要安装程序,只需要直接将软件拷贝到想要存放的目录(通常是/applications)即可。而解安装也就直接删除该.app bundle就解决了。在windows上就没那么容易了(特别是有相当多组件依存关系的软件)。这些都是开发上需要注意的地方,但是开发者多付出一份心力,使用者就会多一份便利,终究会得到用户肯定的。

项目  windows  mac os x 

系统内部编码 unicode (utf-16) unicode (文件系统使用 utf-8, 系统api一般使用 cfstring/nsstring, 内部使用utf-16)

语系处理 区分codepage 不区分codepage

应用程序的设定管理方式 windows registry property list files

ipc的几种方式 com/windows rpc objective-c distributed object/apple event/bsd socket

脚本语言的支持 vbscript/jscript/cscript/dos batch script applescript/perl/ruby/python/shell script 

表三:一些重要的系统特性(摘录)

项目 windows (.net) mac os x (cocoa)

字符串处理 system.string nsstring 

数据结构与容器 system.collections nsarray/nsdictionary/etc. 

http网络存取 system.net system.net,nsurlconnection

xml解译 system.xml nsxmldocument etc.

表四:几个代表性的.net namespace/class在cocoa中的对应class

跨平台的建议 

最后简短分享一些跨平台软件开发所可能遇到的问题。

要同时在windows和mac上开发,有两种可能的思维方式。一种是追求真正的"write once, run everywhere"。此时开发的选择,可能是采用java平台,adobe的air,抑或使用c++搭配像qt这样的跨平台链接库。这三种主流方案各有千秋,但在视觉和用户体验上往往皆无法与原生(native)的mac应用程序相比。

因此,另一个方向则是体认到,要保有windows及mac各自平台的特长,就必须割舍gui跨平台的可能性。也就是说,gui是最无法移植到其他平台的部分。我们能做的是将共通的逻辑部分独立出来,然后开发两套前端接口(frontend)。若以在windows及mac上皆能使用为前提,共通逻辑开发语言的选择就很少了,不是c就是c++。所幸windows和mac上具有平台特色的语言,要和c++结合,也不是那么困难的事(在.net上是透过c++/cli,在mac上是透过objective-c++这两种扩展的语言)。

不过,在开发共享部分的时候,最容易碰到的问题,恐怕还是要如何省下力气去做例如解译xml文件、存取网络这一类不是gui的工作。这类工作的麻烦在于,windows和mac都各自提供了相当便利、但也绝对和平台相依的链接库(例如.net的system.xml,cocoa的nsxmldocument)。在这种情况下,我们也大体有两种选择:不是全部采用跨平台的链接库(例如使用expat来解译xml),就是善用面向对象的抽象化以及abstract factory这样的设计模式(design pattern),让程序逻辑呼叫抽象的接口,然后在于各自平台的版本中藉由呼叫平台相依的api来实现这些对象。

结论 

本文简要地讨论了windows及mac os x在操作系统架构、开发环境、api、图形环境等环节上的相近处与不同的地方,也简单提出了跨平台应用程序开发的两种策略。事实上在两种平台上开发所需要了解的概念跟技能没有太大的不同,两种平台在性能上的差异也不大,但是在实现细节、视觉表现与用户体验上,os x有自身独特的风格与文化。os x软件开发社群常常说要"be a good mac citizen"意思也就在此。了解这些差异和独特性是撰写合宜的os x软件的第一步。

继续阅读