這一陣子在 IRC 上,因為開發 OV 的緣故,經常有機會提到 OS X 程式設計相關的問題。我們經常提及像 Carbon, Cocoa, Objective-C 這類的關鍵詞,也開始有朋友問及,Carbon 是什麼?Cocoa 又是啥?我如果想在 OS X 上開發程式,該從哪下手好?
這篇文章先回答頭兩個問題:Carbon 跟 Cocoa 是什麼?順便也講講這兩者的差別在哪。
簡單地說,Carbon 和 Cocoa 都是 Mac OS X 的 API,也就是在 OS X 上寫程式,所必須用到的程式庫。就好像在 Windows 上要用 MFC 或 Win32 SDK,在 X-Window 上有 Qt/Gtk 等等。
那為什麼又會有「兩套」程式庫呢?熟悉 Windows 程式設計的人可能已經在猜了:「喔,一套是『低階』的程式庫,像是用 C 寫的 Win32 SDK,一套是『高階』程式庫,像是 C++ 寫的 MFC,對吧?」
答案呢,並不完全正確。Carbon 和 Cocoa 是兩套歷史背景不同的 API。因為我從是從 OS X 才開始使用 Mac 的人,講太多歷史背景可能會露餡。英文版 Wikipedia 上倒是有兩篇不錯的文章 (Wikipedia: Carbon / Cocoa )簡介這兩套 API 的故事,有興趣的朋友可以前往閱讀。
不過,我還是就我有限的認識,說明一下 Carbon 和 Cocoa 的背景。Carbon 是 Apple 自 OS 9 時代末期,為了協助開發者將舊程式移植到 OS X ,所提出的 API 架構。當時有個名詞叫「碳化」(Carbonize),意思是把 OS 9 時代的程式,套上 Carbon 重新編譯,成為 OS X 的軟體。大多數 OS X 上的大型應用程式,都是從這條線過來的,例如 Adobe 系列的程式,微軟的 Word, Excel 等,它們全是 Carbon 寫的應用程式。
Cocoa 就比較有傳奇色彩一點。用最簡單最簡單的說法:Cocoa 就是當年 NeXTSTEP 作業系統的 API。Apple 當年決定把 NeXTSTEP 買下,採用 Mach-BSD 為基底的作業系統的同時也引進了這套以 Objective-C 寫成的 API。說 Cocoa 「就是」NeXTSTEP 並不是太誇張的說法,因為所有 Cocoa 的 API ,都是以 "NS" 的名稱開頭的。NS 就是 NextStep 的縮寫。晚近的應用程式,例如 Safari, TextEdit, 可愛的 Adium,就都是用 Cocoa 寫成的了。
那麼,Carbon 和 Cocoa 的關係是什麼?差別又在哪裡?就某些方面來說,Carbon 的確是較「低階」的 API,而 Cocoa 也的確有相當多的實作,底層是呼叫 Carbon。但偶爾也有 Carbon 呼叫Cocoa 的情形。
就 OS X 目前的發展趨勢,Carbon 和 Cocoa 是並行發展的。Carbon 仍然主要供 C/C++ 程式設計者使用,而 Cocoa 主要供 Objective-C/Objective-C++/Java (沒錯,Cocoa 據說有相當好的 Java 支援)的程式設計者使用。由於 Mac 上仍有相當多的重量級軟體使用 Carbon ,Apple 一時三刻間是不可能宣佈 Carbon 淡出的。
所以,與其說 Carbon/Cocoa 是兩套「平行」的 API,不如說它們是互補的 API。Apple 的「搜循功能」API(以及即將在 10.4 Tiger 中導入的 Spotlight 技術),便只有 Carbon 的 C API 可用。至於 Apple 大大有名的 WebKit 和TextView (讓你可以在「十五分鐘內寫一套功能完整的編輯器/瀏覽器」),則只有 Cocoa 套件可用。
介紹到這裡,寫個範例程式吧。就拿字串操作來說好了,下面這個程式示範如何在 Carbon 裡建立一個字串:
char* str = "Hello, world!"; // 我們想提供建立的字串 CFStringRef cfstr = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); // 用字串... CFRelease(cfstr); // 把建立好的字串釋放掉
如果是 Cocoa ,這個程式可以這樣寫:
char* str = "Hello, world!"; NSString* nsstr = [[NSString alloc] initWithCString: str]; // 用字串 [nsstr release];
當然,這是最簡單的操作,這裡是看不出 Carbon/Cocoa 的差別在哪裡。下面這一行程式,則是把 http://www.google.com 的內容,讀進一個字串裡:
NSString* s = [NSString stringWithContentsOfURL: [NSURL URLWithString: @"http://www.google.com"]];
這件事呢,呃,Carbon 是辦不到的。不過,Carbon/Cocoa 有個神奇的地方是:核心的基礎物件,例如字串、陣列等等,是可以互換的!例如某個 Carbon API 需要 CFStringRef 的參數,你可以直接把 NSString* 傳過去。如果某個 Cocoa API 需要 NSArray* 參數,你也可以直接傳 Carbon 的 CFArrayRef 給它。所以,如果你得寫個Carbon的程式去網頁上抓那容,你可以先用上面的程式把網頁抓下來(我有沒有說用 Objective-C 寫 wget 這樣的工具,只要用上面那行,程式就等於寫完了?),然後再把 NSString* s 當成 CFStringRef 來用。如果想把剛剛的內容印出來,你也可以這樣做:
printf ("%s/n", [s UTF8String]);
像 NSString/CFStringRef 這種可以互轉的資料結構設計,Apple 將之稱為「無痛轉接」(toll-free bridging),非常高段。
接下來有兩個常見的問題,第一個問題,我該選擇那套來入手?這個問題,就我參與 OV 開發的經驗,我會說,直接從 Cocoa 開始!Cocoa 再怎麼說,都是比較新的設計,同時也是徹底物件導向的應用程式介面。Apple Mac OS X 上的各種新型程式設計工具,尤其是 Interface Builder ,幾乎全是為 Cocoa 量身訂作的。更何況 Cocoa 有不錯的 Java 支援,也可以簡單呼叫 AppleScript,種種原因加起來,Cocoa 都是發展 OS X 應用程式的首選。
但是接下來的問題便是,那麼,我是不是得為此學習 Objective-C ?這問題簡單的回答是:要,但是很快可以學會。大體上,如果你發展的是 GUI 程式,又多半是用 Interface Builder 拉選單和對話盒,所用到的 Objective-C ,其實份量並不會太多。大多數時候,只需要知道怎麼呼叫 Cocoa 物件的method,也就完全夠用了。
其實,對於已經學會 C++/C#/Java 的人來說,Objective-C 應該是很好上手的。另外,Apple 為了讓 C++ 的程式設計者可以更方便設計程式,還設計出 Objective-C++ 這樣一套語言。簡單地說,就是讓 C++ 可以呼叫 Objective-C 的物件,或是反過來用 Objective-C 來呼叫 C++。
Objective-C 配上 C++ 是相當銳利的工具。OpenVanilla 在開發之初,載入器的 OS X 相依程式碼部份,仍是以 Carbon 為主。但初期為了快速將輸入法模組開發出來,於是採用了 C++/Cocoa/Carbon 混用的模式,也就是使用了 Objective-C++ 並呼叫 Carbon 及 Cocoa。Objective-C++ 非常適合快速原型開發,我們一個晚上寫出了「大易輸入法模組」的原型,隔了一陣子又花了一個晚上「白話字輸入法」的原型,這些都是用 Objective-C++ 寫完的。當然,寫過程式的人都知道,寫出原型跟實際上能完全符合要求,有很大的一段距離,我只是舉例說明 Objective-C++ 對於程式設計的幫助而已。當然,世事難兩全,Objective-C++ 目前只有 Apple OS X 平台能使用(Objective-C 的話倒是拜 GCC 及 GNUstep 之賜,已經到處都有了),而 Cocoa 用多了,就很難習慣其他 API 了,因為真的是太簡單好用。
Cocoa 倒不僅只有 Objective-C, Objective-C++ 及 Java 能使用。Cocoa 已經有 Python 的介面,Perl 的話也有 CamelBones 這套程式庫可以接口。
簡單地說,Cocoa 是會令人上癮的,我一直覺得,Apple 應該要在未來的版本中,將它改名叫 Cocaine (古柯鹼/可卡因)才對。