1. 程式人生 > >Swift快速入門之可選型別與錯誤處理

Swift快速入門之可選型別與錯誤處理

可選型別

可選型別是個什麼東西呢?其實就是把空值與非空值也作為不同的型別來處理。這個空指的是變數值為null,而不是空字串的空,空陣列的空,也不是蒼井空的空。

那麼變數的型別除了傳統的型別之外,還要再說明能不能為空,才構成完整的型別。比如可以為nil的整型變數與不能為nil的整數變數不是一個型別。可以為nil的型別就是可選型別。

如何決定一個變數是否可以為nil呢?用問號,如:var c:Int? ,定義了一個整型變數c,但他是一個可選型別,所以可以為它賦值nil,如:c=nil ,如果沒有問號,則c絕不能賦nil值。

要將一個可選型別轉成非可選型別,需用驚歎號!如:var d:Int = c ,這是不行滴,需這樣:var d:Int = c! 加個驚歎號是為了讓程式設計師明確知道d是個可以為空的傢伙,這裡的轉換需要確保d的值真的不是空的才能保證後面不出現邏輯錯誤,因為後面再用d的時候,不再判斷它是否為空,因為它被認為不可能為空 。如果將d的值賦給c就不用轉換了,其原因不用再解釋了吧?

由於引入了可選型別,帶來了語法書寫上的一些變化。比如使用可選型別時,一般是不是先要判斷一下它是否為空才使用它的值呢?我們可以這樣寫:

if c != nil {
    print("\(c)*100 = \(c! * 100)")
}

你如果不判斷而直接寫print這條語句的話,而此時如果c的值卻真是空的,那麼你再用驚歎號對它進行強制型別轉換的話,這就是要讓程式駕崩的節奏啊。所以先判斷一下才可。

我們還看到輸出語句中第一個c不用型別轉換,因為那裡可以接受nil,而後面的乘法運算就要求c絕不能為空,所以進行了型別轉換。

但是有一種更簡便一點的寫法,如下:

if let x = c {
    print
("\(x)x100 = \(x*100)") }

其意思是:如果c不為空時,就把c的值賦給x,然後輸出語句直接用x就行了,因為x是一個非空型別。如果c為空,則不賦值給x,當然也不會執行輸出語句了。

我們知道 var a:Int 表示a是非可選型別,此時絕不能為a賦nil,而var a:Int? 表示a是可選型別,可以為a賦nil,但還有一種情況,看這種定義變數的方式:

var a:Int! 

可以為此變數賦nil,而同時在需要非可選型別的地方可以不用驚歎後對它進行轉換。慢,腦子有點亂,這又是什麼玩意呢?是這樣的,蘋果公司發現驚歎號用得太多,過意不去了。因為有的變數就是初始化時用個nil,之後在使用過程中絕對是非空的,但每次都轉換太麻煩,於是就提供了這麼一種方式。這與傳統的不區分空值的型別有什麼區別嗎?用這種方式定義的變數與傳統變數一樣,只能你自己保證其值不為空了。

其實用了非可選型別在執行過程中就一定不會變為空了嗎?這可難說,還是得自己保證,不過,現在這種區分是否為空的語法倒是時刻能給你提個醒。

錯誤處理

錯誤都遵守ErrorType協議,例如:

        enum PrinterError: ErrorType {
            case OutOfPaper
            case NoToner
            case OnFire
        }

使用 throw 來丟擲一個錯誤,使用 throws 來標記一個函式可以丟擲錯誤。如果你在一個函式內丟擲錯誤,函式立馬返回,呼叫函式的程式碼可以處理丟擲的錯誤。

func sendToPrinter(printerName:String) throws -> String{
    if printerName == "Never Has Toner" {
        throw PrinterError.NoToner
    }
    return "Job sent"
}

有很多方法處理錯誤。一種方式是使用 do-catch。在 do 程式碼塊中,你在語句的前面用 try 來表明語句能丟擲錯誤。在 catch 塊中,可以直接使用 error 來操作錯誤,你也可以給錯誤物件指定一個不同的名字。

do {
    let printerResponse = try sendToPrinter("Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}

將函式sendToPrinter(:) 的引數改成 “Never Has Toner”, sendToPrinter(:) 函式就會丟擲錯誤。

你可以提供多個catch 塊來處理不同的錯誤 。你在 catch 後面可用的語法與switch中的case 後的語法一樣。

do {
    let printerResponse = try sendToPrinter("Gutenberg")
    print(printerResponse)
} catch PrinterError.OnFire {
    print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError  {
    print("Printer error: \(printerError).")
} catch {
    print(error)
}

另一種處理錯誤的方式是使用 try? 將結果轉成一個可選型別。如果函式丟擲一個錯誤,錯誤會被忽略,但丟擲錯誤的函式的返回值是nil;如果沒丟擲錯誤,函式的返回值是一個包含實際值的可選型別。如:

let printerSuccess = try? sendToPrinter("Mergenthaler")
let printerFailure = try? sendToPrinter("Never Has Toner")