從Objective-C到Swift

edithfang發表於2014-07-11
2014年WWDC大會,蘋果在毫無預兆的情況下,釋出了Swift程式語言。這個語言剛釋出就引起了廣泛關注。我認為拋開Objective-C單獨談論Swift是不準確的。因此我們先從Objective-C談起。

Objective-C語言

Objective-C語言是編寫iOS/Mac程式的主要語言。編寫iOS/Mac程式,除了Objective-C,還可以混合使用C/C++,另外也可以嵌入一些指令碼語言。但在UI部分,使用Objective-C最為直接自然。

Objective-C和C++都是C語言的擴充套件。C++十多年前就已名滿天下,而Objective-C雖然也誕生了二十多年,但在iPhone釋出之前,一直不溫不火。

這兩種語言都支援物件導向,設計理念卻十分不同。C++的物件導向部分更強調型別,需要知道物件的型別才能呼叫物件的函式。而Objective-C則更強調訊息的傳送。另外,C++很看重執行速度,執行時基本不包含型別資訊,整個執行環境是靜態的。而Objective-C則犧牲了一點執行速度,換來更加靈活、完全動態的執行時環境。

Objective-C的缺點

初學iOS開發,第一個門檻就是Objective-C的語法,它的語法跟主流的語言十分不同。有些人甚至覺得它的語法醜陋得無法忍受。另外,Objective-C的程式碼看起來比較囉嗦。如同一個人,看起來怪怪的,不太合群,熟悉之後又顯得太過嘮叨。

Objective-C缺少一些語言的保護機制,成員函式不區分公有私有,且沒有名稱空間。模組與模組之間需要加字首來區分,比如Foundation模組類前面都加上NS字首,表示NextStep。

Objective-C相容C語言。因此C語言很多設計不合理的地方也被引入了Objective-C中。另外,Objective-C沒有采用GC(垃圾回收),它的記憶體管理採用引用計數的方式,後期引入ARC (自動引用計數),這種記憶體管理方式相比GC而言,對程式設計師的要求較高。

當然,Objective-C也有很多優點。甚至可以說Objective-C的某些缺點,從另一角度看,恰恰是它的優點。

Objective-C的優點
  • 動態執行環境,適合UI程式設計
在Objective-C中,如下語法並非單純的函式呼叫,而是向某物件傳送訊息。


Objective-C的執行環境是動態的,可以在執行時判斷出某物件能否響應某種訊息,而不管此物件是什麼型別。

UI程式大框架就是訊息處理。系統接受到某種訊息(如程式啟動了、列表被選中了、手指在螢幕上移動了)之後交給可以響應這個訊息的物件來處理,而不關心這個物件到底是什麼型別。
  • 方便與C/C++ 混合使用
Objective-C中的C擴充套件部分,使用符號@開頭。比如@class、@interface、@"Hello,World"。而它的訊息傳送語法則是使用中括號而不是圓括號。

這種獨特而另類的語法使得Objective-C的程式碼,就算跟C++程式碼混在一起,也很容易被識別出來。此外,將副檔名修改成mm就可以在同一檔案中混合使用Objective-C和C++。兩者直接相互呼叫,不用經過任何間接層。在需要大量使用C++的場合(比如遊戲),這種混編特性很有用。
  • 執行速度相對較快
Objective-C編譯後是機器原生指令,執行時環境也小而緊湊。它採用引用計數的記憶體管理方式,並引入ARC。ARC比GC更容易引起程式設計錯誤,但卻比GC快。而在效能很重要的場合,Objective-C也很容易直接呼叫C/C++程式碼。相對於其他使用虛擬機器、採用GC以及間接呼叫C/C++的移動平臺,速度優勢非常明顯。

Swift和Objective-C的聯絡

蘋果一直在改進Objective-C,預設編譯器由GCC換成了LLVM,並先後加入literal、block、ARC、Module等特性。我認為最大的兩點改進為以下兩點。
  • ARC+弱引用,本質還是引用計數。但從人手呼叫retain、release,轉成編譯器自動插入程式碼是個質的飛躍。
  • block+GCD,block引入函式式風格的程式碼塊,而GCD則大大簡化了非同步程式碼編寫方式。
WWDC之後,我開始思考一個問題,蘋果為什麼不繼續改進Objective-C,而釋出Swift這門新語言呢?這個問題只有蘋果自己知道,其他人只能猜測。
可能的原因有以下幾個方面。
  • Swift表面看起來很簡單,語法跟流行的C#、JavaScript、C++等語言相似,可以吸引更多的開發者。
  • Objective-C因為需要相容C,所以限制了它的改進。而Swift沒有歷史包袱,可以自由採用最新的語言設計研究成果。
  • 設計者的個人品位,Chris Lattner不習慣Objective-C的語法,就去設計了一個新的。當然,這是玩笑話,不要太當真。
Swift雖然是新語言,卻融合了Objective-C的很多特性。讀Swift的文件會發現,Objective-C與Swift的聯絡十分密切。Objective-C使用的很多底層技術,被應用到Swift中。

Swift與Objective-C共用同一套執行時環境

我們編寫程式,讓程式執行起來,被機器執行的程式碼並非全部是由我們自己來編寫的。需要同時執行很多預先寫好的支援性的程式碼,才能讓我們自己的程式碼執行起來。程式並非單獨存在的,執行時處在一定的環境當中。我總聯想到很多小螞蟻在泥土上面爬,而我自己寫的程式只是其中的一隻。

Swift跟Objective-C編譯出的程式程式碼執行在同一套執行環境上面。Swift的型別可以橋接到Objective-C的型別,反之亦然。Swift編寫的程式碼可以呼叫Objective-C編寫的程式碼,反之也一樣。
Objective-C之前積累下來的大量類庫,實現不用改寫Swift就可以直接呼叫。

同一個工程,可以同時使用Swift和Objective-C

Objective-C在一端,Swift在另一端,兩端經中間檔案進行橋接。橋接檔案包含Objective-C的標頭檔案,編譯時自動轉成Swift可以識別的形式。Swift就可以使用Objective-C的類和它的函式。

在Swift的類中,加上@objc(類名)的字樣,Objective-C也可以使用Swift編寫的類。但Swift跟C++的相互呼叫,需要Objective-C來封裝。

總的來說,共同使用Swift和Objective-C/C++還算方便,但已不能將幾種語言的程式碼,混寫在同一檔案。大概是因為Swfit的語法不像Objective-C那樣獨特,混寫難以將Swift的程式碼識別出來。

Swift骨子裡大多與Objective-C一樣

Objective-C出現過的絕大多數概念,比如引用記數、ARC、屬性、協議、介面、初始化、擴充套件類、命名引數、匿名函式等,在Swift中繼續有效(可能只是換了個術語)。我自己將Swift看成是Objective-C的一塊大大的語法糖,其他人可能有不同感受。

Swift大多數概念與Objective-C一樣,也有些概念在Objective-C找不到對應,比如泛型。Swift中將那種操作寫一次就可以作用多個型別的語法叫做Generics(泛型)。

程式語言和語法

新語言出來時,總會有種聲音認為語言只是工具,他們會覺得重要的是思想,而看輕工具的作用。認為程式語言是工具本身並沒有大錯,但語言並非普通的工具,而是思維工具,不能忽視語言對思維的影響。程式語言就如同數學符號,數學符號也是種思維工具,好的數學符號會幫助使用者思考,更奇妙的是似乎符號本身也會思考。用0~9這些阿拉伯數字進行計算時會感到一切都很自然,似乎數字本身在計算,而不是人在做計算。

儘管程式語言的語法很重要,但語法背後的概念更重要。當明白語法背後的概念後,從一門語言切換到另一門有著相同概念的語言會很容易。但語法會影響表達,理論上每門語言都可以表達任何概念。不過當某種概念在某門語言中很難表達出來時,我們就會傾向於不使用它,這種概念在那門語言的社群就難以被人熟知。

程式語言用來表達思路,表達起來是否自然是很重要的。最理想的是不多不少、剛剛好,寫起來自然,讀起來更需要自然。並非功能越多的語言越好,而是應該越能幫助思考,越容易表達思路的語言越好。

Swift背後的概念大多跟Objective-C差不多。但Swift吸收了很多其他語言的語法,同一個概念,寫起來比Objective-C簡潔得多、自然得多。從表達的角度來說,Swift比Objective-C優秀。

Swift的語法

蘋果出了本很不錯的語法教程來詳細描述Swift的語法。在這裡,我們只挑一些有意思的點來討論。

簡潔

Swift的語法給人第一感覺就是簡潔乾淨。如果省略掉也不引起歧義,那符號基本上可以被省略。

我們來看下面這個例子:



Swift寫起來,有點像指令碼語言。這裡沒有出現型別定義,有人覺得它就是指令碼語言,是解釋執行的。而實際上Swift是真正的編譯語言,通過型別推導,變數的型別可以確定下來。既然省略掉型別也不引起歧義,也就可以省略了。上面的例子中,傳統的句末分號可以省略;迴圈中的()也可以省略;迴圈1是C語言中的傳統寫法;i作為計數變數;迴圈2是迴圈1的等價寫法,因為根本不用關心計數變數,因此可以直接寫成“_”。

不再相容C語言,修正Objective-C中容易出錯的地方

例子1

在C/Objective-C中,if、while、for之後的判斷式並不需要一定傳入布林型別。也可以傳入整型、指標等型別,只要非0就為真,並且賦值是有副作用的。比如:



上面程式碼返回a的數值,這樣就有可能出現將判斷:



錯寫成:



為避免這個問題,有種變通寫法:



這種寫法被稱為Yoda表示式,因為《星球大戰》中的Yoda大師喜歡使用這樣奇特的倒裝句子。

Swift強制要求if、while、for後面判斷式子一定需要傳入布林型別,並且賦值沒有副作用。因此寫成以下這種判斷就會編譯錯誤。Yoda表示式這種變通寫法再也沒有必要。


例子2

還是拿判斷來說。在C/Objective-C中,if、while、for之後的語句假如只有一行,是可以省略掉大括號的。比如:



當後面的人修改程式碼,或多人修改同一程式碼再合併時,可能會在if後面直接插入一行,這樣就一定會goto fail了:



在Swift中,強制要求if、while、for之後一定需要寫大括號,不管是不是隻有一行。這樣就避免了上述問題。

不難看出,Swift的語法設計使一些C/Objective-C常見錯誤不可能再出現。

常用型別整合到語言裡面,而非以庫的形式提供

有幾種型別,幾乎每個專案中都使用到了,分別是:整數、浮點數、布林型、空值;字串;陣列、字典。

對這些型別的支援,每個語言都有所不同。有些老舊的語言不支援浮點;有些語言中,布林和空值是整數的一種;有些語言中,整數和浮點數合併成一類,統稱為Number;有些語言中,並沒有字串、字典、陣列;有些語言中,字典和陣列歸成一類。

語言不提供的型別多數以庫的形式提供。比如在C++中,語言內部並沒有提供字串、字典、陣列,但這些型別卻出現在了標準的庫中。使用庫的形式來提供常用型別,優點是減少語言自身的複雜度,另外庫可以自由替換。而採用語言直接支援的方式,使用起來會很簡潔流暢。Swift中的常用型別整合到了語言內部。 陣列寫成:



字典寫成:



這種寫法在原來的Objective-C中就已出現,現在融入到了Swift中。

此外,Swift還支援另外兩種型別:option和tuple。option表示有值或根本無值;tuple則表示多個值,它可以從函式中返回多個值,或一行程式碼交換兩個值。

在C++中,這兩種型別也採用庫的方式來提供。std::tie與std::tuple結合,可以達到返回多重值的效果,但顯得語法噪音多、不夠方便,所以也比較少用。

支援多種程式設計泛式

將Swift分拆開,可大致分成以下部分:基本型別、控制流、函式定義和呼叫、函數語言程式設計、物件導向、泛型。

實際上,Swift的內容包含很多,也可以說很複雜,並非表面上看起來那樣簡單。Swift從語法上直接支援現代流行的程式設計泛式,包括:結構化程式設計、函數語言程式設計、物件導向、泛型。每個部分Swift都有些閃光點,在此無法一一述說。

值得一提的是,我特別喜歡它的函數語言程式設計部分。接下來我們直接拿文件的一個排序例子,看看它是怎麼簡化程式設計的。

最開始版本,傳遞函式



使用閉包



推匯出引數型別,引數型別省略



推匯出返回型別,return省略



省略掉閉包裡面變數



假如閉包是函式的最後一個型別,可以將{}提到外面



使用操作符函式,最後版本


很好地支援內部DSL

有一種程式設計風格不太好歸類,就是將程式拆分成“描述+解釋”。解釋部分寫一次,其他地方使用描述式的語句,而不是命令式的語句。

內部DSL,通常利用主語言的語法特性創造出一套寫法來寫一些描述性的語句。這些語句組合起來就像一門新語言似的。舉個從Ruby那裡借過來的例子。假如計算幾小時之後的秒數,C語言中大致會寫成:



而現在Swift中定義了擴充套件:



因此可以寫成(分別是3小時後的秒數和3小時前的秒數):



同理,也可以寫成:



這種寫法看起來跟原來的命令式寫法完全不同。這些語句是描述性的,而原來的Objective-C做不到這一點。估計Swift以後會冒出大量這樣風格的庫。至於這種風格到底好不好,要看具體情況。據我所知,比較方便定義內部DSL的語言,原來有C++、Ruby、Lisp,而現在又多了Swift。

Swift的語法還有很多有意思的地方。比如屬性的中get、set寫法;switch case中的模式匹配;泛型中的型別約束等。這裡不一一細說了。

Swift的爭議

下面列出一些Swift爭議性較大的地方:
  • 東抄西抄一些語法、大雜燴,沒有自己的原創;
  • 沒有一些private、protected之類的許可權控制;
  • 沒有采用GC(垃圾回收),卻採用ARC(自動引用計數);
  • 沒有異常處理;
  • 是否可以跳過Objective-C,直接學習Swift。
對這些爭論,我們很難作出客觀的評價,更不可能武斷地下結論。我感覺Swift這門語言還是比較有意思的(有些人寫程式只是單純為了好玩)。

這裡特別提一下異常處理。以往我一直覺得異常處理是很好的特性,現在覺得異處理常並非一定是好的。異常處理的問題在於發生異常的地方與處理異常的地方分離開來,真正處理時難以收集到足夠的資訊。異常也容易濫用,很多初學者的Java程式碼,只丟擲異常而不處理,或乾脆在最外層接收所有異常,再列印出異常資訊。這樣也就失去了異常處理的意義。Objective-C中也有異常處理的功能,但實際上幾乎沒有人使用。對於Swift,採用多返回值加上option的方式來處理錯誤可能會更好。

視覺化程式設計

WWDC釋出會上演示了Swift的Playground功能。敲入程式碼,立即有反饋。當我們每一步操作都得到實時的反饋時,我們的做法會有很多不同,做出的東西也會有不同。視覺化程式設計放到最後,並非是表示它不重要,而是它實在太重要了,只是目前有能力評說的人還不多。關於這部分內容, Bret Victor有幾個演講視訊和文章,建議大家看看。

Swift還有很多主題,只能靠大家自己研究了。最後拿出本次WWDC的Slogan與大家分享:Write the code,Change the world。

本文作者黃兢成ios軟體工程師
來自:CSDN
相關閱讀
評論(0)

相關文章