隨著剛剛結束的 WWDC 2015 蘋果發布了一系列更新,這其中就包括了令人振奮的 Swift 2.0
。 這是對之前語言特性的一次大幅的更新,加入了很多實用和方便的元素,下面我們就一起來看看這次更新都包括了什么。
將 println 函數統一為 print
現在我們在代碼中輸入 println("xxx")
這樣的調用時,編譯器就會報錯:

都已經變成通用的 print
方式了:
print("xxx")
語言的結構性更強
比如在 Swift 1.2
中,我們要判斷某元素是否在數組中,我們會用到 contains
函數:
var apps = ["Youtube","Google","Facebook"]
if contains(apps, "Google") {
println("ok")
}
而在 Swift 2.0
中,變成了直接調用數組對象 apps
的 contains
方法進行這個操作,這種調用方式更加的結構化,和面向對象化:
if apps.contains("Google") {
print("ok")
}
對于字符串操作,也是一樣,如果在 Swift 1.2
中,我們要獲得字符串的長度,我們會這樣:
let str = "Swift App"
let c = count(str)
而在 Swift 2.0
中,我們只需調用 str
對象的 count
方法,即可完成字符串數量的統計:
let str = "Swift App"
let c = str.characters.count
do-while 循環語法關鍵詞的改動
do-while
循環語句在 Swift 2.0
中也發生了變化。以往,我們代碼中用到 do-while
循環,會這樣處理:
var counter = 5
do {
print(counter)
counter--
} while counter > 0
而在 swift 2.0
中, do-while
循環中的 do
關鍵字被替換成了 repeat
,如果我們在 Swift 2.0
還使用 do
關鍵字的話,就會導致編譯錯誤:

Swift 2.0
中我們使用 repeat-while
循環:
repeat {
print(counter)
counter--
} while counter > 0
在 Swift 2.0
中,之所以將 do-while
循環變成了 repeat-while
循環,是因為 do
關鍵字另有他用,這個在后面我們會講到。
新增了 #available 標注來進行多版本兼容性支持
在以往的開發經歷中,最讓我們頭疼的一個問題就是 API
的版本兼容。比如我們用了一個 iOS 8
引入的方法,但我們的 App
運行在了 iOS 7
的設備上,如果這時不手動進行系統版本檢測的話, 我們的 App
就會直接的崩潰掉。而對于這種 API
編譯器不會給我們任何的提示,只能靠著人工去逐個處理,不僅麻煩,而且很容易造成遺漏,導致嚴重的崩潰問題。
Swift 2.0
新引入的 #available
機制,就解決了這一問題。 新的 Swift
編譯器,會在編譯的時候就進行檢測,舉個例子,比如 UIAlertController
這個類是 iOS 8.0
引入,但我們的項目設置的 Deployment Target
是 iOS 7.0
, 這時候我們在編譯代碼的時候,編譯器就會給出我們這樣的警告:

看到了吧,在 Swift 2.0
中,編譯器會自動幫我們檢測哪些 API
需要進行版本兼容判斷,非常的強大吧。這樣就減去了我們很多麻煩,并且大大減少了 App
出錯的概率。
編譯器幫我們檢測到問題之后,接下來我們就要處理這個問題,也就是進行系統版本的條件判斷,也就是通過 #available
來判斷:
if #available(iOS 8.0, *) {
let alert = UIAlertController(title: "test", message: "app", preferredStyle: .Alert)
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
} else {
let alert = UIAlertView(title: "test", message: "app", delegate: nil, cancelButtonTitle: "ok")
alert.show()
}
我們看上面的代碼 if #available(iOS 8.0, *)
用于檢測當前的系統版本是否在 iOS 8
或以上。如果是的話,那么我們就使用 UIAlertController
。 否則,我們還繼續使用 UIAlertView
。
現在編譯我們的代碼,即可編譯通過。 #available
這個特性的提供,算是對我們現有的開發方式的一個改進。也體現了 Swift
的安全性為本的核心理念。

感覺腦洞小開哦~
錯誤處理 try,catch 語句的增加
Swift 2.0
中提供了對錯誤處理更好的支持,增加了 try-catch
語句。現在我們可以這樣進行異常處理操作了:
do {
let content = try NSString(contentsOfFile: "/file/path/str.txt", encoding: NSUTF8StringEncoding)
} catch {
print("read content fail")
}
是不是發現了 do
關鍵字了呢,Swift 2.0
中將 do
關鍵字用到了異常處理塊中。還有一點和其他語言不同的是,這里的 try
關鍵字是寫在具體調用代碼行上面的。也就是說,那個語句會有可能拋出異常,我們才在哪個語句前面加上 try
關鍵字。這種方式有一個好處。就是我們可以一目了然的看到那些代碼會拋出異常。而不是將所有代碼都混在 try-catch 語句塊中。
throws 和 throw 關鍵字,以及自定義異常類型
我們還可以對我們自己定義的函數聲明異常拋出,使用 throws
關鍵字:
func requestImage(urlString:String) throws -> UIImage? {
if let url = NSURL(string: urlString) {
if let data = NSData(contentsOfURL: url) {
return UIImage(data: data)
}
}
return nil
}
在返回值類型聲明前面加上 throws
關鍵字,即可將我們的函數聲明為拋出異常類型:
func requestImage(urlString:String) throws -> UIImage?
接下來我們還需要定義我們要拋出的異常類型。我們可以通過 ErrorType
類型的枚舉聲明來定義我們自己的異常類型:
enum RequestImageError : ErrorType {
case NetworkError
case URLError
}
我們定義了兩個異常類型,NetworkError
表示網絡錯誤,URLError
表示 url 錯誤。 我們還需要在我們的方法中拋出這些異常:
func requestImage(urlString:String) throws -> UIImage? {
if let url = NSURL(string: urlString) {
if let data = NSData(contentsOfURL: url) {
return UIImage(data: data)
} else {
throw RequestImageError.NetworkError
}
} else {
throw RequestImageError.URLError
}
}
現在調用這個方法的時候,就可以通過 try,catch 來處理異常情況了:
do {
try requestImage("http://swiftcafe.io/images/qrcode.jpg")
} catch RequestImageError.NetworkError {
print("network error")
} catch RequestImageError.URLError {
print("url error")
}
guard 關鍵字
Swift 2.0
中新引入了一個叫做 guard
的關鍵字用于條件判斷處理。舉個例子來說,我們以前在代碼中對函數的參數進行驗證的時候,可能會用到這種方法:
func printName(firstName:String?, _ lastName:String?) {
if firstName != nil {
if lastName != nil {
print("\\\\(lastName!) \\\\(firstName!)")
}
}
}
我們對每一個參數都用一個 if
語句來判斷,這樣的代碼結構讀起來結構不是很清晰,并且如果參數的數量比較多的話,if
語句的嵌套層數就會很深,導致可讀性的降低。那么為了減少嵌套層數,我們還可以這樣:
func printNameByIf(firstName:String?, _ lastName:String?) {
if firstName == nil {
return
}
if lastName == nil {
return
}
print("\\\\(lastName!) \\\\(firstName!)")
}
我們在函數的開始,用 if 語句來判斷各個參數。當這些判斷失敗的時候,會直接 return
。只有當所有的判斷都通過,才會執行函數中真正的代碼。這樣做,解決了之前 if
嵌套的可讀性的問題,好了很多。
但這樣依然有它的問題。比如,這樣的可讀性依然不是很好,不能充分顯示這個 if
語句的意圖。并且,對于 Optional 類型的值,也沒有進行很好的處理。
基于這些情況,Swift 2.0
中引入了 guard
關鍵字,我看來看一下如何用 guard
來實現這個方法:
func printNameByGuard(firstName:String?, _ lastName:String?) {
guard let first = firstName else {
return
}
guard let last = lastName else {
return
}
print("\\\\(first) \\\\(last)")
}
我們這里用到了 guard
關鍵字,來進行參數條件的判斷。比起之前的 if
判斷,代碼的可讀性更強,并且意圖更加明確。 guard
還有一個好處就是對于 Optional 的解包的作用域是在函數內完全可見的。 比如上例中,我們解包出的 first
和 last
,可以在 guard
執行完后,繼續使用。
gurad
關鍵字,除了用在參數判斷返回的場景下,還能用在很多別的地方:
guard app.characters.count > 0 else {
throw InputError.NameIsEmpty
}
guard #available(iOS 8, *) else {
return
}
defer 關鍵字
在了解 defer
關鍵字之前,讓我們先了解一個比較常見的例子:
func getFileContent(path:String) -> NSString {
guard path.characters.count > 0 else {
showGetFinished()
return ""
}
if NSFileManager.defaultManager().fileExistsAtPath(path) {
do {
let content = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
showGetFinished()
return content
} catch {
showGetFinished()
return ""
}
} else {
showGetFinished()
return ""
}
}
上面的函數就是一個簡單的讀取文件內容的方法,我們注意看一下里面的 showGetFinished()
方法,在多個分支都被調用了。實際上它的邏輯只是干一件事,就是在函數結束的時候顯示一下讀取完成消息。顯然我們這里的代碼在每個 if
調用 return
返回之前,都調用了 showGetFinished()
方法。在語法上,這樣的調用是沒問題的。但從業務邏輯角度考慮,其實這樣做是比較笨拙的。但我們又受制于語法限制,只能寫出很多這樣形態的代碼。
這就是 Swift 2.0
中引入 defer
關鍵字的作用了。我們來看看 Swift 2.0
中是怎么處理這個問題的:
func getFileContentDefer(path:String) -> NSString {
defer { showGetFinished() }
guard path.characters.count > 0 else {
return ""
}
if NSFileManager.defaultManager().fileExistsAtPath(path) {
do {
let content = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
return content
} catch {
return ""
}
} else {
return ""
}
}
我們看一下上面的代碼,所有分支中的 showGetFinished()
調用都不見了,我們只在函數的第一行看到一句 defer { showGetFinished() }
。 如果我們執行這個函數,就會發現無論代碼走到哪個分支,defer
語句塊中的 showGetFinished()
都會被調用。
defer
語句就相當于在它的作用域中執行一個收尾工作,又叫做稍后執行,比如我們例子中的函數,showGetFinished()
方法就符合這個邏輯,在函數執行完成后,做一些收尾的操作(比如這個例子里面要顯示一下狀態信息)。
defer
從程序語法結構上,做了一個改進,以往我們為了達到類似的目的需要不斷的通過 if-else
分支來實現的邏輯,可以能夠更加清晰和簡潔的表達出來。

恩,不錯不錯。喜笑顏開~
defer
語句塊不僅能夠在函數中使用,它幾乎可以在任何 {..}
語句塊中使用:
func branch() -> String {
var str = ""
str += "1"
defer { str += "2" }
let counter = 3;
if counter > 0 {
str += "3"
defer { str += "4" }
str += "5"
}
str += "6"
return str
}
我們這次,在 branch
函數和它里面的 if
語句塊中都用到了 defer
語句塊。我們函數最終返回的 str 中的內容是:
13546
這個結果和各位想到的結果是否一樣呢?
Swift 2.0
是一個重大的改進,包括了很多的優化與改動,這里面只介紹了其中一些比較顯著的優化與更新。更多的更新內容在后期還會為大家繼續整理的哦。從這次更新中我們不難看到 Swift
依然秉持著它基于類型安全已經更現代化的開發方式的理念。在現在產品都注重用戶體驗的同時,相信 Swift
也會給我們這些開發者更好的開發體驗。
更多精彩內容可以關注微信公眾號:
swift-cafe