BKJIA譯文】無論結果是好是壞,蘋果打造的全新語言都將讓我們以自己的方式處理開發工作。
對於任何一位蘋果公司之外的粉絲而言,Swift語言的突然面世究竟會帶來怎樣的後續影響都實在難以斷言。就在本屆WWDC大會的主題演講中,蘋果在不斷放出我們預期之內訊息雖然其中一些細節確實帶來了驚喜)的同時突然話鋒一轉,公布了其打造的Objective-C現代替代方案——也就是Swift。事實上,Swift程式設計語言曾經在史蒂夫•喬布斯創立NeXT公司之後被短暫用於項目開發。
Swift絕不是那種“今年年底之前將正式推出”類型的公告內容。就在同一天,iBooks商店當中上架了一份長達550頁的Swift語言開發指南蘋果官方Swift教程9天完成漢化 可線上閱讀)。開發人員們同時還能夠體驗Xcode 6 beta測試版,它允許我們利用這款新語言進行應用程式開發。總而言之,讓Swift與Cocoa工具包順暢協作所必需的一切要素及變更都已經準備到位,只剩開發人員們蒞臨品鑒了。
儘管還沒有正式利用Swift進行代碼開發,但我們已經通讀了整篇語言使用指南並查閱了蘋果提供的程式碼範例。我們接下來要做的就是親手使用這款語言,並體會蘋果到底希望利用它實現哪些目標。
我們當初為什麼要使用Objective-C?
當年NeXT公司建立之初,物件導向的編程機制尚未得到廣泛普及,甚至沒有幾種語言能夠真正實現這一構想。在那個時候,Objective-C可能是我們所能選擇的最理想的方案——它一方面能夠保持原有C代碼的可用性以及開發人員的傳統使用習慣,另一方面則在此基礎上添加了物件導向層。
但事實最終證明,NeXT公司是惟一一家使用該語言的主流廠商。從某種角度講,這也帶來一些積極的因素,畢竟該公司可以在心無旁騖的情況下從零開始、專註打造屬於自己的整套Objective-C開發環境。順理成章,任何一位打算利用該語言進行軟體開發的使用者最終也不可避免地會用到NeXT的方案。舉例來說,Objective-C當中的很多“語言特性”其實根本就不屬於真正的語言特性;它們其實來自NeXT創造的基本類,也就是NSObject。此外,Coca當中的一部分設計模式,例如delegates的存在,也需要用到Objective-C當中的introspection功能——它的作用是以安全方式檢測某個對象是否會對特定訊息作出響應。
Objective-C那極為有限的使用面也帶來負面影響,即迫使該語言不得不面向利基市場。在蘋果繼承了Objective-C時,技術人員馬上著手為開發人員提供備用方案,也就是Carbon庫,這樣做是為了保證其開發方式更接近於傳統的Mac軟體開發流程。
iPhone SDK的迅速走紅讓一切有了轉機,由於它只允許開發人員選擇Objective-C,這款物件導向的語言也開始席捲世界各地。似乎在一夜之間,開發 人員們開始前赴後繼地使用Objective-C,其中很多人甚至將自己積累了多年的其它程式設計語言使用經驗拋在了一邊。這對於蘋果來說無疑是個天大的好訊息,但卻也帶來了一些問題。並不是每位開發人員都心甘情願地使用Objective-C並樂在其中,而蘋果隨後採取我們都熟知的方式解決這個問題——宣布Mac開發工作的未來將由Cocoa這款Objective-C架構承擔。
Objective-C到底有什麼問題?
Objective-C與蘋果一直擁有難以置信的默契與出人意料的和諧。通過嚴格控制運行時並編寫屬於自己的編譯器,這家技術巨頭已經有能力在規避部分源自NeXT時代的語言局限性的同時為其加入一系列新功能,例如屬性、垃圾收集機制以及垃圾收集機制的替代產物——自動引用計數。
但有些情況真的無法改變。由於仍然算是進行了一定程度擴充的C語言衍生產物,Objective-C只能利用C的方法來追蹤複雜物件,也就是指標——指標基本上是對象佔用的記憶體位址的第一個位元組。一切的一切,從NSString到最為複雜的表視圖,都需要利用其指標進行傳遞及通訊。
在大多數情況下,這種機制並不會產生問題。我們在編寫複雜的應用程式時,基本不會意識到自己已經在不知不常見中用到了指標。但在某些極為罕見的情況下,這種機制也會弄糟一切並嘗試訪問錯誤的記憶體位址,從而導致程式崩潰或者帶來意料之外的安全性漏洞。事實上,C語言的其它一些特性也會造成類似的弊端;開發人員必須小心檢查自己的代碼長度並忍受相關限制,否則這些代碼很可能遊盪到記憶體中的其它隨機位置。
除了這類先天不足的問題,Objective-C也隨著時間推移而漸顯老態。技術的不斷髮展讓其它程式設計語言擁有了一些很難被移植到C語言這類古董開發方案中的卓越特性。最典型的例子就是所謂“generic”。在C語言中,如果我們希望對整數及浮點數值進行同樣的數學去處,則必須為它們各自編寫一個獨立的函數——再加上專門用於其它無符號長整數以及雙精確度浮點數值的額外函數。在generic的協助下,大家可以單獨編寫一個函數,即可輕鬆保證所有數值都被編譯器正確認定為數字。
蘋果公司顯然可以在Objective-C的文法中添加一些重要特性——closure就是個很好的例子——但我們尚不清楚他們是否能隨心所欲地實現此類特性添加。而且C語言的天然屬性意味著它總是存在安全風險,畢竟所有穩定性與安全性任務都由一套單一而缺乏認真考量的編碼器承擔。可以說,是時候改變這一切了。
不過為什麼不採取更簡單的路線,直接選擇另一種現有語言呢?這是由於Objective-C與Cocoa架構之間存在著緊密的聯絡,Objective-C所使用的一系列設計模式能夠顯著提高該架構的執行效率。而大部分現有主流替代方案都沒辦法以如此簡潔的方式與現有Cocoa架構順暢協作。有鑒於此,Swift應運而生。
拍攝於2014WWDC現場
好吧,但為什麼是現在?
原因有幾點。隨著將開發體系向LLVM遷移,蘋果已經牢牢控制住自己的運行時與工具鏈。這使得方案變化在實施以及充分瞭解其後果)方面變得更為簡單。蘋果同時也在語言開發方面積累到了豐富的經驗,並成功為Objective-C帶來屬性、自動引用計數以及closure等新特性。
作出這些變化也讓蘋果初步感受到了開發人員未來可能對調整作出怎樣的反應。當蘋果引入垃圾收集機制並向開發人員們宣稱“這是一套出色的方案”時,大家的反饋可以說有好有壞。而後來蘋果引入自動引用計算並表示“這代表著未來”時,開發人員們開始以更快的速度適應這一新情況。蘋果決定腰斬64位Carbon的決定無疑協助開發人員們提早預計到了新語言發布這一天的到來。)
所有這些早期變動協助Swift中部分新特性的推出掃清了障礙。我們不妨回憶一下:closure與自動引用計數的面世時間還不太長,變數的處理方式與Objective-C的屬性可以說相當接近。再有,因為蘋果公司嚴格掌控著一切,因此同一套運行時可以並行支援Swift與Objective-C,即運行遺留代碼與全新語言順暢對接。總而言之,也許在五年前公布這樣的決定會帶來毀滅性的後果,但如今Swift已經具備了順利接班的群眾基礎。
我們可能永遠不知道蘋果到底從多久以前就開始策劃Swift程式設計語言的面世,但綜合蘋果在過去五年當中的實踐經曆,大家就會發現他們一直在不斷積累經驗、作出試探性動作並觀察開發人員社區到底對這類變動擁有怎樣的容忍度。
大家懂我的意思嗎?
如果大家打算編寫一款新型程式設計語言,那麼面臨的首要難題就是在其易用性與可讀性之間作出取捨。作為最為淺顯的示範執行個體,我們選取了Swift開發人員說明文檔中的原始範例。如果我們寫出以下代碼,即使是對編程並不太熟悉的讀者朋友也能從中看出點端倪。
- if thisIsTrue {
-
- that = that + 50
-
- } else {
-
- that = that + 20
-
- }
然而如果直接使用下面這種代碼錶達形式,恐怕大部分人都會感到一頭霧水:
- that = that + (thisIsTrue ? 50 : 20)
上面兩部分代碼錶達的其實是同樣的意思。第二種明顯更簡潔、能夠大大降低輸入負擔,而第一種則更加淺顯、易於理解。程式設計語言的設計者們必須作出一系列決定來作好這兩方面的權衡工作。這些決定最終可能給程式設計語言的命運帶一深遠影響,畢竟可讀性影響到新手能否快速入門、理解代碼樣本所要表達的意思,或者在注釋及為糟糕的情況下允許開發人員快速接手項目。
除非大家有辦法弄明白這些括弧的含義,否則Objective-C在可讀性方面的表現實在比較糟糕。如果大家編寫了一條函數,旨在將兩個字串之間的文本提取出來,那麼在大多數程式設計語言整個實現過程通常如下所示:
- myString.getTextBetweenBrackets( leftBracket, rightBracket);
但在實際使用中,這些變數往往不可能使用如此隨意的命名方式。除非大家能把這條函數記在腦子裡,否則肯定需要提醒自己這兩個括弧的添加順序。Objective-C選擇增加一部分錄入成本以擺脫造成歧義的風險,由此編寫出的同含義代碼如下所示:
- [myString getTextBetweenLeftBracket: leftBracket andRightBracket: rightBracket];
寫明每個參數的實際作用很有價值,這意味著我們可以增加參數的數量,但同時也會令變數名的自我解釋能力隨之下降。Swift則頗有種二者混合的意思,但同時也確切保留了下面這種表達方法:
- myString.getBracketedText ( LeftBracket: leftBracket, rightBracket: rightBracket );
這是一種非常重要的處理方式,在這裡可讀性得到了保證,不過每一位對Objective-C上述此類特性感到不滿的開發人員同樣也會對Swift心生怨恨。也許我們應該說可讀性‘能夠’得到保證,畢竟Swift也允許大家跳過將方法符號添加到括弧當中的過程。蘋果建議大家正確寫明這些額外的文本,但我猜很多人根本不會鳥他們那一套。)
而在其它很多方面,Swift的可讀性確實比較堪憂。最重要的是,單一字元的存在——例如一個驚嘆號或者問號——有可能給整個函數調用鏈產生巨大影響,甚至導致整行代碼的執行效率完全改變。大家可以通過函數定義的方法來應對此類問題,例如設定預設值並為參數設定內部與外部名稱,但這樣做意味著我們必須認真閱讀該函數的內容、以保證自己確切瞭解它的實際作用。
下面再來看另一個摘自Swift程式設計語言開發指南的樣本,我們看看要如何定義一套行星枚舉:
- case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
我們只需要為第一個條目添加一套索引。如果沒有其它操作,編譯會假設我們希望由1開始進行遞增並自動依此進行。這樣做的效果很好,也能協助程式員節約很多時間。不過如果大家碰巧忘記了土星是太陽系第六顆行星——或者是碰巧處理的是一套相對難於記憶的內容列表——各位可能會發現自己需要將大把時間浪費在確定哪個數字到底對應什麼條目身上。
這種特性對於我們在可讀性與易寫性方面的個人取向到底有什麼影響?在我們深入使用這套語言並閱讀更多樣本代碼之前,答案尚無法確定。就目前的情況看,大量Swift代碼恐怕很難通過直接閱讀瞭解其含義,但我們覺得這一點在實際使用中並非關注的首要重點。大部分程式員可能更青睞於Swift那相對壓縮化的文法,但該語言的靈活性可能意味著人們會有意無意編寫出在實際運行中沒有問題、但其他使用者卻很難理解的代碼內容。