從Windows的角度看Mac OS X軟體開發

Web開發者發表於2016-05-13

  如果原來從事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之類的環境才能辦到。

  Apple幾年前有則廣告是「把其他牌子的UNIX送進/dev/null裡」(用過UNIX的朋友應該不難體會其中的吹噓意涵)。平心而論,OS 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在架構上的對照

  開發語言與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++來撰寫。

表二:開發語言與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上就沒那麼容易了(特別是有相當多元件依存關係的軟體)。這些都是開發上需要注意的地方,但是開發者多付出一份心力,使用者就會多一份便利,終究會得到使用者肯定的。

表三:一些重要的系統特性(摘錄)

表四:幾個代表性的.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軟體的第一步。 >>資源下載

  作者介紹

  Lukhnos是臺灣的Mac開發者。他是繁體中文輸入法計畫 OpenVanilla的發起人之一,也是該計畫的核心成員。  除了參與多種open source軟體計畫開發外,Lukhnos也是石磐軟體公司 (Lithoglyph Inc.)的創辦人。該公司致力於Mac相關桌面應用軟體 開發並接受開發委託。公司網址是http://lithoglyph.com

相關文章