Swift 可選型別

Cocoma發表於2018-06-05

對於使用Objective-C開發iOS的程式設計師來說,物件是否為nil在編寫程式的過程中程式設計師不太關心,直接使用就可以,在一些需要判斷的地方在判斷,當轉為Swift開發時,首先頭疼的問題就是可選型別,到底何時使用?,何時使用!總是拿不太準,下面就詳細的講解一下

Swift 可選型別

一、什麼是可選型別

可選型別伴隨著Swift誕生,在原有的Objective-C語言中不存在,究其原因,是因為Swift是型別安全的語言,而OC則是弱型別語言,OC中 str字串既可以是nil,也可以是字串,而Swift中,這兩種狀態是不能同時存在的。 OC和Swift對於nil的解釋也是不太一樣的

1.Objective-C中的nil:表示缺少一個合法的物件,是指向不存在物件的指標,對結構體、列舉等型別不起作用(會返回NSNotFound) 2.Swift中的nil:表示任意型別的值缺失,是一個確定的值,要麼是該型別的一個值要麼什麼都沒有(即為nil)

在Swift中Optional(可選型別)是一個含有兩種情況的列舉,None 和 Some(T),用來表示可能有或可能沒有值。任何型別都可以明確宣告為(或者隱式轉換)可選型別。當宣告一個可選型別的時候,要確保用括號給 ? 操作符一個合適的範圍。

1.1可選型別的宣告

var optionalStr: String? = "swift語言可選型別"//宣告可選型別字串,並賦初值
var opStu:Student? //宣告可選opStu物件,賦初值nil
複製程式碼

注意:在型別和 ?之間沒有空格

1.2 強制解析(拆包)

當你確定自定義的可選型別一定有值時,可以使用操作符(!)進行強制解析,拿到資料,歎號表示"我知道一定有值,請使用它",但是當你判斷錯誤,可選值為nil時使用(!)進行強制解析,會有執行錯誤。

var myStr:String? = nil
myStr="強制解析,一定有值"
if myStr != nil {
    print(myStr!)//使用!進行強制解析
}else{
    print("字串為nil")
}
複製程式碼

執行結果

強制解析,一定有值
複製程式碼

1.3 自動解析

在最初的宣告時使用?修飾,當你希望它自動解析是可以用!代替?來修飾,這樣在變數使用時就不需要加!來強制拆包,它會自動解析。

var myStr:String! //使用!修飾
myStr="自動解析"
if myStr != nil {
    print(myStr)
}else{
    print("字串為nil")
}
複製程式碼

執行結果

自動解析
複製程式碼

1.4 可選繫結

A.使用可選繫結,擺脫了頻繁的判斷是否為nil在賦值,但是使用可選繫結(optional binding)來判斷可選型別是否包含值,如果包含就把值賦給一個臨時常量或者變數。可選繫結可以用在if和while語句中來對可選型別的值進行判斷並把值賦給一個常量或者變數。 格式:

if let tempStr = someOptional{
    tempStr//如果someOptiona有值的話,就會賦值給tempStr,然後使用
}
複製程式碼
var myStr:String?
myStr="可選繫結"
if  let tempStr = myStr {
    print(tempStr)
}else{
    print("字串為nil")
}
複製程式碼

執行結果

可選繫結
複製程式碼

B.在可選型別判斷中還有guard let的用法,guard名為守衛的意思。guard let和if let剛好相反,guard let守護一定有值。如果沒有,直接返回

let name: String? = "張三"
let age: Int? = 10

guard let nameNew = name,
      let ageNew = age
else {
        print("姓名 或 年齡 為nil")
        return
}

print("姓名 或 年齡 為\(name),\(age)")//執行到這裡是一定有值的
複製程式碼

1.5 as! 與 as? 的型別轉換

在寫Swift程式碼時總是拿不準到底使用哪個,有時會根據編輯器的提示去Fix,但是這樣不能很好的解決問題。 1.as!使用場合

向下轉型(Downcasting)時使用。由於是強制型別轉換,如果轉換失敗會報 runtime 執行錯誤。就是說強制從父類轉換成子類

class Animal {}
class Cat: Animal {}
let animal :Animal  = Cat()
let cat = animal as! Cat
複製程式碼

2.as?使用場合

as? 和 as! 操作符的轉換規則完全一樣。但 as? 如果轉換不成功的時候便會返回一個 nil 物件。成功的話返回可選型別值(optional),需要我們拆包使用。由於 as? 在轉換失敗的時候也不會出現錯誤,所以對於如果能確保100%會成功的轉換則可使用 as!,否則使用 as?

let animal:Animal = Cat()

if let cat = animal as? Cat{
    print("cat is not nil")
} else {
    print("cat is nil")
}
複製程式碼

二、可選型別的使用場景

2.1 函式或方法的返回型別為可選型別

在方法中返回值如Int?、String?、(Int, String)?、[Int]?、[Int: String]?等

func returnOptionValue(value: Bool) -> String? { // 返回型別為可選String型別
    if value {
        return "返回型別是可選型別值" // 如果為真,返回Int型別的值1
    } else {
        return nil //返回nil
    }
}

let optionValue = returnOptionValue(value: true) // 要用可選繫結判斷再使用,因為returnOptionValue為String?可選型別
if let value = optionValue {
    print(value)
} else {
    print("none value")
}
複製程式碼

執行結果

返回型別是可選型別值
複製程式碼

返回型別為閉包可選

func returnOptionalFunc(value: Bool) -> (() -> (Void))? { // 返回型別為可選型別的閉包
    if value {
        return { () in
            print("返回型別是可選型別閉包")
        }
    } else {
        return nil
    }
}

let possibleFunc = returnOptionalFunc(value: true) // 要用可選繫結判斷再使用,因為possibleFunc 為可選型別的閉包,型別為() -> (Void)
if let aFunc = possibleFunc {
    print(aFunc())  // 注意增加()呼叫閉包,因為沒有引數則是空括號
} else {
    print("none func")
}
複製程式碼

執行結果

返回型別是可選型別閉包
複製程式碼

2.2 可選型別在類或結構體中的運用

可選型別在類中的使用

class PossibleClass {
    var someValue: Int
    var possibleValue: String? // 可選儲存屬性,預設值為nil
    init(someValue: Int) { // 構造方法中可以不對possibleValue屬性初始化
        self.someValue = someValue
    }
}

let someClass = PossibleClass(someValue: 4)
複製程式碼

可選型別在結構體中的運用

struct PossibleStruct {
    var someValue: Int
    var possibleValue: String? // 可選儲存屬性,預設值為nil
    // 結構體中可以自定義一個init構造器對屬性初始化,也可以不自定義
}

let someStruct = PossibleStruct(someValue: 4, possibleValue: nil)
複製程式碼

2.3 可選型別在構造器中使用

class PossibleStructInit {
    let someValue: String
    init?(someValue: String) { // 可失敗構造器
        if someValue.isEmpty { return nil } // 如果例項化為空串,則返回nil(即例項化失敗)
        self.someValue = someValue
    }
}

let oneStruct = PossibleStructInit(someValue: "構造器中使用") // abc不是空串,oneStruct為PossibleStructInit?可選型別
if let one = oneStruct { // 使用if let可選繫結判斷
    print(one.someValue)
} else {
    print("none value")
}
//輸入結果為:構造器中使用

let twoStruct = PossibleStructInit(someValue: "") // 傳參為空串
if let two = twoStruct {
    print(two.someValue)
} else {
    print("none value")
}
//輸入結果為:none value
複製程式碼

2.4 可選型別在錯誤處理中使用(try!與try?)

1.try?會將錯誤轉換為可選值,當呼叫try?+函式或方法語句時候,如果函式或方法丟擲錯誤,程式不會發崩潰,而返回一個nil,如果沒有丟擲錯誤則返回可選值。 2.使用try!可以打破錯誤傳播鏈條。錯誤丟擲後傳播給它的呼叫者,這樣就形成了一個傳播鏈條,但有的時候確實不想讓錯誤傳播下去,可以使用try!語句

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
// 上述語句中在執行loadImage方法時如果執行失敗,使用try!來禁用錯誤傳遞,會有執行錯誤導致App崩潰
複製程式碼
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction() // x可能正常返回一個Int型別的值也有可能丟擲一個錯誤異常,使用時對x用if let可選繫結判斷
複製程式碼

相關文章