Swift 2 初學者指南

Swift 2包括了一些新的功能,像是改善錯誤處理、協定擴展(protocol extension)以及可行性檢查(availability check)。其中Swift 2一項最大的改變是它將在2015年底變成開源。這篇初學者指南會讓你了解一下它帶來了哪些新的功能。


去年 Apple 帶來了 Swift,一個為針對 iOS 以及 OS 的全新程式語言。當它第一次宣布時,就跟其他開發者一樣。我非常的興奮,因為這宣稱是一個既快且安全的語言。跟預期一樣,這家公司今年在 WWDC 導入了 Swift 2。這篇初學者指南會讓你了解一下它帶來了哪些新的功能。

Swift 2今年一樣快速前進中。我們認為 Swift 是未來20年作為應用程式以及系統的主力程式語言,我們認為它他應該可以在任何地方都能用到且每一個人都能使用它。

– Craig Federighi, Apple的軟體工程資深副總

其中Swift 2一項最大的改變是它將在2015年底變成開源。倘若你錯過了 WWDC 的一些重點,或者你消息落後的話,你沒看錯:Swift準備開源。 這是一件大事。Apple會在 OSI 標準許可證(OSI compliant license)底下公開釋出Swift 原始碼,包含編譯器(compiler)與標準函式庫(library)。Apple也即將讓原始碼能與Linux介接。開發者將能夠在Linux使用Swift撰寫程式,進而對這個程式語言的發展做出貢獻。事實上對於語言的開發是值得鼓勵的。或許未來的某一天,你便能夠使用 Swift 來開發Android App了。

除了這個令人興奮的消息,Swift 2引進一些新的功能,包括了改善錯誤處理、協定擴展(protocol extension)以及可行性檢查(availability check)。

do-while 現在變成 repeat-while

我們先從基本的開始。這個經典的 do-while 迴圈現在重新命名為 repeat-while, 例如:

如同待會這節所述,這個 do 關鍵字現在也應用了其他語言的功能。 將do改為repeat,讓你能夠容易的辨識這是一個迴圈。

for-in where 子句

另外一個基本功能的導入是針對 for-in 陳述導入where子句。 你現在可以在 for 使用 where 子句。 當你對陣列執行迴圈,譬如對於符合規則的項目才能繼續。

以上的例子中,它只會列印大於 100 的數字。

if-case 模式匹配

當 Swift 第一次釋出時,switch 陳述也做了相當的更新。 不只可以使用 switch 來匹配任何資料型態, switch 也同時支援了範圍(range)與模式(pattern)匹配, 以下是一個範圍匹配的示範:

在 Swift 2, 除了 switch外,你可以使用 if case 來執行範圍匹配:

if case 也可以應用到模式匹配。這裏我使用元組(tuples)為例:

第一個if case 子句測試使用者的歲數是否超過18。 這下底線表示「這個值我不在乎」, 這裏我們只確認這個使用者的年紀。 這個 else if case 是用來測試郵件是否為空值。 跟 switch 例子一樣,在使用 if case 時你可以將值暫時綁定。 我們綁定值給 email 常數並透過 where 子句執行匹配。

Guard 介紹

Swift導入了 guard 關鍵字,根據 Apple的文件。 guard 的敘述如下:

一個 guard 陳述,就像if 陳述一樣,依照一個表達式的布林值來執行陳述(statement)。為了讓guard陳述後的程式被執行,你使用一個 guard 陳述來取得必須為真(true)的條件。

在我繼續解釋 guard陳述前,我們直接進到這個例子:

這裏我們建立一個printInfo 函數來顯示一篇文章的標題。 不過我們只是要印出一篇超過上千文字的文章資訊。 因為變數是 optional。我們使用 if let 來確認是否optional有包含一個值。倘若這個optional是 nil,我們會顯示一個錯誤訊息。 倘若你在 Playgrounds 執行程式,它會顯示文章的標題。

通常 if-else 陳述依照這個模式:

你可能會這注意到,倘若你必須測試更多條件,它會嵌入更多條件。這樣的程式沒有什麼錯。但是以可讀性來看,你的程式看起來會很亂,因為有很多嵌套條件。

因此 guard 陳述因應而生。 guard 的語法如下所示:

倘若定義在 guard 陳述內的條件不匹配, else 後的程式便會執行。 換句話說,倘若條件符合,它會略過 else 子句並且繼續執行程式。

倘若你使用 guard 重寫以上的範例程式,會更簡潔:

有了guard你就將重點放在處理不想要的條件。甚至,它會強迫你一次處理一個狀況,避免有嵌套條件。如此一來程式便會變得更簡潔易讀。

錯誤處理

在開發一個 App 或者任何程式,不論好壞,你需要處理每一種可能發生的狀況。很清楚地,事情可能會有所出入。譬如說,倘若你開發一個連線到雲端的 App,你的 App 必須處理網路無法連線或者雲端伺服器故障而無法連接的情況。

在目前的 Swift版本,它缺少了適當的處理模式。舉例來說,處理錯誤條件的處理如下:

當呼叫一個方法時,可能會造成失敗,通常是傳遞一個 NSError 物件(像是一個指標)給它。倘若有錯誤,這個物件會設定對應的錯誤。然後你就可以檢查是否錯誤物件為 nil ,並且給予相對的回應。

這是在 Swift 1.2 處理錯誤的的做法。

注意: NSURLConnection.sendSynchronousRequest() 在 iOS 9 已經不推薦使用,但是因為大部分的讀者比較熟悉這個用法,所以在這個例子我們才使用它。

try / throw / catch

在 Swift 2 內建了使用 try / throw / catch keywords關鍵字,像例外(exception)的模式。相同的程式會變成這樣:

現在你可以使用 do-catch 陳述來捕捉(catch)錯誤並處理它。你可能有注意到。我們有放一個 try 關鍵字在呼叫方法前面,有了 Swift 2.0新錯誤處理模式的導入,一些方法會丟出錯誤來表示失敗。當我們調用一個 throw 方法,你需要放一個 try 關鍵字在前面。

你要怎麼知道是方法丟出一個錯誤?當你在內建編輯器輸入方法時,這個 throw 方法會標示出一個throws關鍵字。

throwing-methods

現在你應該了解要如何呼叫一個 throw 方法並捕捉錯誤,那要如何指示一個可以丟出錯誤的方法或函數呢?

想像你正在規劃一個輕量型的購物車,客戶可以使用這個購物車來短暫儲存並針對購買的貨物做結帳,但是購物車在以下的條件下會丟出錯誤:
* 購物車只能儲存最多五個商品,否則的話會丟出一個 cartIsFull 的錯誤。
* 結帳時在購物車至少要有一項購買商品,否則會丟出 cartIsEmpty的錯誤。

在 Swift,錯誤是由遵循 ErrorType 協定的型態值來呈現。 通常是使用一個列舉(enumeration)來規劃錯誤條件,在這裡,你可以建立一個採用 ErrorType 的列舉,如以下購物車錯誤的例子:

對於購物車,我們建立一個 LiteShoppingCart 別來規劃它的函數。以下為其程式段:

倘若你更進一步看一下這個 addItem 方法,你可能會注意到這個 throws 關鍵字。 我們加入 throws 關鍵字在方法宣告處來表示這個方法可以丟出錯誤。在實作中,我們使用 guard 來確保全部商品數是少於5個。否則,我們會丟出 ShoppingCartError.cartIsFull 錯誤。

要丟出一個錯誤,你只要撰寫throw關鍵字,接著是實際錯誤。針對 checkout 方法。我們有相同的實作。 倘若購物車沒有包含任何商品,我們會丟出 ShoppingCartError.emptyCart 錯誤。

現在,我們來看結帳時購物車是空的會發生什麼事。 我建議你啟動 Xcode 並使用 Playgrounds來測試程式。

因為checkout 方法會丟出一個錯誤, 我們使用 do-catch 陳述來捕捉錯誤, 倘若你在 Playgrounds 執行以上的程式, 它會捕捉 ShoppingCartError.emptyCart 錯誤並印出相對的錯誤訊息,因為我們沒有加入任何項目。

現在插入以下的程式至 do 子句, 在 checkout 方法前面:

在這裡我們加入全部6 個商品至shoppingCart物件。同樣的,它會丟出錯誤,因為購物車不能存放超過五個商品。

當捕捉到錯誤時,你可以指示一個正確的錯誤( 例如 ShoppingCartError.cartIsFull )來匹配, s因此你就可以提供一個非常具體的錯誤處理。 另外,倘若你沒有在 catch 子句指定一個模式(pattern), Swift會匹配任何錯誤並自動地綁定錯誤至 error 常數。 最棒的做法還是應該要試著去捕捉由throw方法所丟出的特定錯誤。 同時, 你可以寫一個 catch 子句來匹配任何錯誤。這可以確保所有可能的錯誤都有處理到。

defer

倘若你有寫過Java 程式語言,它提供一個相似的例外處理模式,稱作 try-catch-finally. 指定在 finally 子句的程式,不管錯誤為何都會執行。

Swift 2 導入 defer 關鍵字來提供一個相似的功能,不管錯誤為何,定義在 defer 區塊中的程式在目前的範圍完成之前會執行。我們繼續使用購物車為例來說明。譬如說,倘若你想要在結帳時印出購物車內的全部商品數,你可以插入defer 陳述至 checkout 方法,如下所示:

現在當你呼叫 checkout 方法, 不管他是否能正常完成或有錯誤,它會列印「Number of items in shopping cart」的訊息。

倘若你在Playgrounds測試 checkout 方法,如下所示:

你會在主控台(console)見到這兩個訊息:

defer 陳述對清除操作特別有用:

協定擴展 (Protocol Extension)

在 Swift 1.2,你可以使用擴展(extension),在目前的類別(class)、結構(structure)或列舉(enumeration)加入新的功能。譬如說,你可以使用擴展加入新的功能至 String 類別:

你不只可以幫類別建立擴展。Swift 2 可以讓開發者將擴展應用至協定型態。在Swift 2 之前,協定只包含了方法與屬性宣告。在一個類別在採用這些協定時,你需要提供你自己的實作。倘若你之前使用過UITableView的話,我相信你已經非常熟悉協定的實作。

有了協定擴展,你可以在目前的協定中加入方法或屬性。當你想要擴展協定的功能,這個功能非常強大。除了這個,你可以在協定的這些方法中以擴展來提供預設的實作。

我們來參考以下範例:

我們宣告一個協定稱作 Container。在協定中,它有宣告一個稱作 numberOfItems的方法。對於任何採用 Container協定的類別,它必須實作這個方法來回傳容器中全部的項目數。

上的例子中,我們有三個類別模型,一個工具箱(ToolBox)、一個是紙袋(PaperBag)以及一個籃子(Basket)。每一個類別都採用Container協定,以回傳儲存在特定容器中的項目數。倘若你在Playgrounds,它會印出儲存在工具箱的項目數(也就是 6)。透過協定的使用,你可以很容易地以其他採用 Container 的協定的物件來指定 container 變數:

你可能會注意到,在這些類別中 numberOfItems 方法的實作是相同的。在 Swift 2 之前,你不能在協定中提供預設的實作。但是現在你可以透過協定擴展來達成:

有了預設的實作,這三個類別可以簡化如下:

他們都具備了 numberOfItems 方法的預設實作。倘若預設實作不符合你的需求,你還是可以覆寫它。以下面這個例子為例:

假設你想要加入一個稱作 randomItem的新方法,可以隨機回傳一個項目至協定中。在 Swift 1.2,你需要加入這方法至Container 協定。

在這裏,所有採用Container 協定的類別都需要變更並提供 randomItem 方法的實作。

有了協定擴展的導入,你可以輕易的加入方法至擴展中,而所有遵循這協定的類別可以自由實作它。

是不是很棒?Swift 2大方的擴展了協定的範圍。這裏我只說明了協定擴展的基礎。你可以進一步利用協定導向程式( protocol-oriented programming )的力量,我不準備在這篇文章介紹。倘若你有興趣,你可以看一下這部很棒的 WWDC 影片.

可行性檢查 (Availability Check)

倘若所有的使用者被強迫更新到最新版的iOS版本,這會讓開發者更輕鬆些,但事實沒這麼理想,你的App必須應付不同iOS 的版本(例如 iOS 7 與 iOS8 )。倘若你只在你的App使用最新的版本的API,這樣一來,在其他較舊版本的iOS會造成錯誤。當使用只能在最新的iOS版本才能使用的API,你必須要在使用這個類別或呼叫這個方法前做一些驗證。

在Swift 2之前,沒有可行性檢查(availability check)的標準。例如,NSURLQueryItem 類別只能在iOS 8使用。倘若你在其他的iOS版本使用,你會得到錯誤並且可能造成 App 閃退。要避免這樣的錯誤,你可以像以下這樣執行可行性檢查:

這是檢查類別是否存在的一個方式。從Swift 2開始,它內建了 API 可行性的檢查。你可以輕易地定義一個可行性條件,因此這段程式將只會在某些 iOS 版本執行。如下面這個例子:

這邊在一個if陳述中使用 #available 關鍵字。 在這個可行性條件,你指定了要確認的OS版本(例如 iOS 8、OSX 10.10)。星號(*)是必要的,並指示了 if 子句所執行的最低部署目標以及其他OS的版本。 以上面例子來說, if 的主體將會在 iOS 8 或以上版本執行,以及其他平台,像是 watchOS。

相同地,你可以使用 guard 代替 if 來檢查 API 可行性,如下面這個例子:

那麼如果你想要開發一個類別或方法,可以讓某些OS的版本使用呢?Swift 2 讓你在類別/方法/函數 應用 @available 屬性來指定你的目標平台與OS 版本。 舉例來說,你正在開發一個類別稱作 SuperFancy,而它只能適用於iOS9或之後的版本,你可以像這樣應用 @available

倘若你試著在Xcode 專案使用這個類別來支援多種iOS裝置,Xcode會告訴你以下的錯誤:

availability-check-warning

不再使用 println()

在Swift 2以前,是使用 println() 函數來列印訊息至主控台或log檔。 而最新版本的 Swift,我們只能使用 print() 來輸出所寫的訊息。 Apple 已經結合了 println()print() 函數為一個。 print() 函數印出你的訊息。倘若你不輸出某些東西不想要加上換行,你可以設定appendNewline 參數為 false,如以下例子:

總結

我希望你喜歡這篇Swift 2 初學者指南。有一些功能我還沒有研究。你可以進一步參考這部 WWDC影片 來學習更多 Swift 2的內容。至今許多公司還是使用 Objective C 作為iOS主要程式語言。或許你也是還在使用 Objective C。我堅定相信 Swift 的學習是正確,你將會見到越來越多公司開始雇用 Swift 程式設計師。事實上,所有的在2015 WWDC 的範例都是以 Swift 來撰寫。所以倘若你想要開始新的專案,是時候以 Swift 來開發了。

譯者簡介:王豪勳 -渥合數位服務創辦人,畢業於台灣大學應用力學研究所,曾在半導體產業服務多年,近年來專注於協助客戶進行App軟體以及網站開發,平常致力於研究各式最軟硬體技術,擁有多本譯作。
原文A Beginner’s Guide to Swift 2

<p>軟件工程師,AppCoda 創辦人。著有《iOS 10 App 程式設計實力超進化實戰攻略》、《iOS 9 App 程式設計實力超進化實戰攻略》、《養成iOS 8 App程式設計實力的25堂課》,以及《iOS 8 App程式設計進階實力的30項關鍵技巧》。曾任職於HSBC, FedEx等公司,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda,致力於iOS程式教學,產品設計及開發。</p>

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This