Swift3.0可選類(lèi)型(Optional)傾心總結(jié),看這個(gè)就夠了??????

本篇主要對(duì)Swift3.0版本的可選類(lèi)型經(jīng)常使用的場(chǎng)景及注意點(diǎn)加以總結(jié),當(dāng)做以后可以隨時(shí)翻譯的資料;因?yàn)楸酒獮樽约壕幋a時(shí)候的總結(jié),難免會(huì)有遺漏,希望廣大讀者加以補(bǔ)充

Swift3.0 Optional類(lèi)型

首先我們先看下Objective-C與Swift語(yǔ)言對(duì)于可選nil的不同理解:

Objective-C中的nil:表示缺少一個(gè)合法的對(duì)象,是指向不存在對(duì)象的指針,對(duì)結(jié)構(gòu)體、枚舉等類(lèi)型不起作用(會(huì)返回NSNotFound)
Swift中的nil:表示任意類(lèi)型的值缺失,是一個(gè)確定的值,要么是該類(lèi)型的一個(gè)值要么什么都沒(méi)有(即為nil)
一、申明可選常量或變量
let status: Int? = 1   // 申明可選Int類(lèi)型的常量,初始值為1 
var defaultAddress: String? = "江蘇南京"   // 申明可選String類(lèi)型的變量,初始值為"江蘇南京"
var student: Person? // 申明可選Person(自定義的類(lèi))的變量,初始值為nil

注意:Int?Int不相同,Int?表示可選的Int類(lèi)型,可以賦值為nil,而Int不可以賦值為nil

二、使用"!"強(qiáng)制解析獲取可選類(lèi)型的值(不建議直接使用)
var defaultAddress: String? = "江蘇南京"
if defaultAddress != nil { // !=或==可以用來(lái)判斷是否為nil
    print("您的地址是\(defaultAddress!)") // 使用!強(qiáng)制解析
} else {
    print("對(duì)不起,您不存在地址信息")
}

var student: Person?
print("學(xué)生為\(student!)") // XCode會(huì)提示運(yùn)行錯(cuò)誤,因?yàn)閟tudent初始值為nil,強(qiáng)制解析不行
三、使用可選綁定獲取可選類(lèi)型的值(建議的用法)
var defaultAddress: String? = "江蘇南京"
if let address = defaultAddress { // 如果defaultAddress有值或類(lèi)型轉(zhuǎn)換成功,則將值賦值給address直接使用
    print("您的地址是\(address)")  // 使用address代替defaultAddress,且不需要加!強(qiáng)制解析
} else {
    print("對(duì)不起,您不存在地址信息")
}
四、隱式解析可選類(lèi)型(用于申明時(shí)肯定有初始值,但后面可能為nil)
var mobileNumber: Int64! = 13912345678 // 第一次申明有初始值
print("您的電話號(hào)碼是\(mobileNumber)") // 不需要使用!強(qiáng)制解析
// 打印內(nèi)容:**您的電話號(hào)碼是****Optional(13912345678)**
// 還是不建議直接強(qiáng)制解析,因?yàn)閷?shí)際項(xiàng)目中可能中間已經(jīng)對(duì)該值做了改變,若為nil則會(huì)運(yùn)行錯(cuò)誤導(dǎo)致APP崩潰
if let number = mobileNumber { // 建議的做法
    print("您的電話號(hào)碼是\(number)")
// 打印內(nèi)容:**您的電話號(hào)碼是****13912345678**
} else {
    print("您沒(méi)有記錄電話號(hào)碼")
}
五、空合運(yùn)算符(用于判斷變量或常量是否為nil)
// 空合運(yùn)算符:a ?? b 判斷a是否為nil,若a不為nil對(duì)a解封,否則返回b的值
var status: Int? // 申明可選Int類(lèi)型的變量status,初始值為nil
status ?? 0 // 因?yàn)閟tatus為nil,則返回0
// ?? 即為以下if else的縮寫(xiě)
func testOption() -> Int {
    let status: Int? = 1
    if status == nil {
        return 0
    } else {
        return status!
    }
}
六、函數(shù)/方法返回類(lèi)型為可選類(lèi)型
A:返回值為可選類(lèi)型的值(如Int?、String?、(Int, String)?、[Int]?、[Int: String]?等)
func returnPossibleValue(value: Bool) -> String? { // 返回類(lèi)型為可選String類(lèi)型
    if value {
        return "返回類(lèi)型是可選類(lèi)型值" // 如果為真,返回Int類(lèi)型的值1
    } else {
        return nil // 如果為假,返回nil
    }
}

let possibleValue = returnPossibleValue(value: true) // 要用可選綁定判斷再使用,因?yàn)閜ossibleValue為String?可選類(lèi)型
if let value = possibleValue {
    print(value)
} else {
    print("none value")
}
B:返回值為可選類(lèi)型的類(lèi)(如URL?、自定義Person?等)
class SomeClass {
    var someValue: Int
    init?(someValue: Int) { // 可失敗構(gòu)造器
        if someValue == 1 { return nil }
        self.someValue = someValue
    }
}

func returnPossibleClass(value: Bool) -> SomeClass? { // 返回的類(lèi)實(shí)例可能為nil
    if value {
        return SomeClass(someValue: 1) // 返回的為nil
    } else {
        return SomeClass(someValue: 2) // 返回的SomeClass?實(shí)例,不為nil
    }
}
C:返回值為可選類(lèi)型的閉包(如(()-> (void))? )
func returnOptionalFunc(value: Bool) -> (() -> (Void))? { // 返回類(lèi)型為可選類(lèi)型的閉包
    if value {
        return { () in
            print("返回類(lèi)型是可選類(lèi)型閉包")
        }
    } else {
        return nil
    }
}

let possibleFunc = returnOptionalFunc(value: true) // 要用可選綁定判斷再使用,因?yàn)閜ossibleFunc 為可選類(lèi)型的閉包,類(lèi)型為() -> (Void)
if let aFunc = possibleFunc {
    print(aFunc())  // 注意增加()調(diào)用閉包,因?yàn)闆](méi)有參數(shù)則是空括號(hào)
} else {
    print("none func")
}
七、可選類(lèi)型在類(lèi)或結(jié)構(gòu)體中的運(yùn)用
A:可選類(lèi)型在類(lèi)中的運(yùn)用
class PossibleClass {
    var someValue: Int
    var possibleValue: String? // 可選存儲(chǔ)屬性,默認(rèn)值為nil
    init(someValue: Int) { // 構(gòu)造方法中可以不對(duì)possibleValue屬性初始化
        self.someValue = someValue
    }
}

let someClass = PossibleClass(someValue: 4) // 實(shí)例化對(duì)象時(shí)不需要對(duì)possibleValue初始化,someClass實(shí)例中:someValue為4,possibleValue為nil

注意:類(lèi)中所有屬性都需要有默認(rèn)值。屬性可以在申明時(shí)賦予初始值,也可以在構(gòu)造方法中賦予初始值;子類(lèi)繼承父類(lèi)時(shí),必須先給自身屬性先初始化后再繼承父類(lèi)構(gòu)造方法初始化。一般的,出于安全的因素,子類(lèi)的屬性都賦予初始值或直接定義為可選類(lèi)型。

B:可選類(lèi)型在結(jié)構(gòu)體中的運(yùn)用
struct PossibleStruct {
    var someValue: Int
    var possibleValue: String? // 可選存儲(chǔ)屬性,默認(rèn)值為nil
    // 結(jié)構(gòu)體中可以自定義一個(gè)init構(gòu)造器對(duì)屬性初始化,也可以不自定義
}

let someStruct = PossibleStruct(someValue: 4, possibleValue: nil)

調(diào)用默認(rèn)的逐一構(gòu)造器

說(shuō)明:從上圖可看出實(shí)例化,PossibleStruct會(huì)調(diào)用默認(rèn)逐一構(gòu)造器,其中possibleValue可傳String類(lèi)型的值或nil

八、可選類(lèi)型在構(gòu)造器中的運(yùn)用

概念:可失敗構(gòu)造器是 一個(gè)類(lèi)、結(jié)構(gòu)體或枚舉類(lèi)型的對(duì)象,在構(gòu)造過(guò)程中有可能失敗;這里所指的“失敗”是指,如給構(gòu)造器傳入無(wú)效的參數(shù)值,或缺少某種所需的外部資源,又或是不滿(mǎn)足某種必要的條件等。

A:結(jié)構(gòu)體的可失敗構(gòu)造器
struct PossibleStructInit {
    let someValue: String
    init?(someValue: String) { // 可失敗構(gòu)造器
        if someValue.isEmpty { return nil } // 如果實(shí)例化為空串,則返回nil(即實(shí)例化失敗)
        self.someValue = someValue
    }
}

let oneStruct = PossibleStructInit(someValue: "abc") // abc不是空串,oneStruct為PossibleStructInit?可選類(lèi)型
if let one = oneStruct { // 使用if let可選綁定判斷
    print(one.someValue)
} else {
    print("none value")
}
// 結(jié)果打印 abc

let twoStruct = PossibleStructInit(someValue: "") // 傳參為空串
if let two = twoStruct {
    print(two.someValue)
} else {
    print("none value")
}
// 結(jié)果打印 none value
B:枚舉的可失敗構(gòu)造器
enum PossibleEnmuInit {
    case East, West, South, North // 不帶原始值,帶原始值也可以使用可失敗構(gòu)造器
    init?(director: Character) {
        switch director {
        case "E":
            self = .East
        case "W":
            self = .West
        case "S":
            self = .South
        case "N":
            self = .North
        default:
            return nil // 如果實(shí)例化時(shí)不是E/W/S/N字符,則返回nil(即實(shí)例化失敗)
        }
    }
}

let oneEnmu = PossibleEnmuInit(director: "E")  // 傳值E,返回PossibleEnmuInit?可選類(lèi)型
if let one = oneEnmu { // 使用可選綁定判斷
    print(one)
} else {
    print("none value")
}
// 結(jié)果打印 East

let twoEnmu = PossibleEnmuInit(director: "A")  // 傳值A(chǔ),返回PossibleEnmuInit?可選類(lèi)型
if let two = twoEnmu { // 使用可選綁定判斷
    print(two)
} else {
    print("none value")
}
// 結(jié)果打印 none value
C:類(lèi)的可失敗構(gòu)造器
class Product {
    let name: String
    init?(name: String) {   
        if name.isEmpty { return nil }
        self.name = name
    }
}

class CartItem: Product {
    let quantity: Int
    init?(name: String, quantity: Int) {
        if quantity < 1 { return nil } // 實(shí)例化時(shí)如果quantity小于1,則立即終止構(gòu)造過(guò)程
        self.quantity = quantity
        super.init(name: name) // 如果name為空串,則立即終止構(gòu)造過(guò)程
    }
}

if let twoSocks = CartItem(name: "sock", quantity: 2) {
    print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 打印 "Item: sock, quantity: 2”

if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
    print("Unable to initialize zero shirts")
}
// 打印 "Unable to initialize zero shirts”
九、可選類(lèi)型在可選鏈中的運(yùn)用

概念:可選鏈為一種可以在當(dāng)前值可能為nil的可選值上請(qǐng)求和調(diào)用屬性、方法及下標(biāo)的方法。如果可選值有值,那么調(diào)用就會(huì)成功;如果可選值是nil,那么調(diào)用將返回nil。多個(gè)調(diào)用可以連接在一起形成一個(gè)調(diào)用鏈,如果其中任何一個(gè)節(jié)點(diǎn)為nil,整個(gè)調(diào)用鏈都會(huì)失敗,即返回nil。

class Person { 
    var name: String
    var room: Room? // 不是每個(gè)人都買(mǎi)房,定義為可選類(lèi)型
    init(name: String) {
        self.name = name
    }
}

class Room {
    var roomAddr: String
    var roomArea: Int
    init(roomAddr: String, roomArea: Int) {
        self.roomAddr = roomAddr
        self.roomArea = roomArea
    }
}

let xiaoming = Person(name: "xiaoming") // 此時(shí)xiaoming實(shí)例中屬性:name為"xiaoming",room為nil
if let address = xiaoming.room?.roomAddr, let area = xiaoming.room?.roomArea { // 使用?可選鏈?zhǔn)秸{(diào)用,如果含有值則返回該值(不需要手動(dòng)強(qiáng)制解析),如果沒(méi)有值則返回nil
    print("\(xiaoming.name)的房子地址在:\(address), 面積:\(area)")
} else {
    print("\(xiaoming.name)沒(méi)有房子")
}
// 打印:xiaoming沒(méi)有房子

xiaoming.room = Room(roomAddr: "南京雨花臺(tái)區(qū)", roomArea: 95) // 定義xiaoming實(shí)例的room屬性
// 再次調(diào)用以上可選綁定,打印 xiaoming的房子地址在:南京雨花臺(tái)區(qū), 面積:95
十、可選類(lèi)型在錯(cuò)誤處理中的運(yùn)用

概念:錯(cuò)誤處理即為響應(yīng)錯(cuò)誤一級(jí)從錯(cuò)誤中恢復(fù)的過(guò)程

A:一般的錯(cuò)誤處理寫(xiě)法(一般使用于需要對(duì)不同的返回結(jié)果較為清晰的捕捉并做相應(yīng)的處理)
// 操作錯(cuò)誤處理枚舉
enum OperationError: Error {
    case ErrorOne
    case ErrorTwo(String) // 帶關(guān)聯(lián)值的枚舉屬性
    case ErrorOthers
}

// 錯(cuò)誤處理拋出
func throwDriver(num: Int) throws {
    if num == 1 {
        throw OperationError.ErrorOne
    } else if num == 2 {
        throw OperationError.ErrorTwo("數(shù)據(jù)類(lèi)型錯(cuò)誤")
    } else {
        throw OperationError.ErrorOthers
    }
}

do {
    print("使用do-catch捕獲錯(cuò)誤")
    try throwDriver(num: 2) // 使用try嘗試請(qǐng)求
    print("未捕獲到錯(cuò)誤")
} catch OperationError.ErrorOne { // catch去捕捉是否又throw出來(lái)的錯(cuò)誤
    print("捕捉到錯(cuò)誤:ErrorOne")
} catch OperationError.ErrorTwo(let message) { // 可以使用let 獲取枚舉關(guān)聯(lián)值
    print("捕捉到錯(cuò)誤:ErrorTwo" + message)
} catch OperationError.ErrorOthers {
    print("捕捉到錯(cuò)誤:ErrorOthers")
}
// 打印結(jié)果如下:
// 使用do-catch捕獲錯(cuò)誤
// 捕捉到錯(cuò)誤:ErrorTwo數(shù)據(jù)類(lèi)型錯(cuò)誤
B:錯(cuò)誤處理(try!)(不建議使用,可能會(huì)導(dǎo)致App崩潰)
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
// 上述語(yǔ)句中在執(zhí)行l(wèi)oadImage方法時(shí)如果執(zhí)行失敗,使用try!來(lái)禁用錯(cuò)誤傳遞,會(huì)有運(yùn)行錯(cuò)誤導(dǎo)致App崩潰
C:錯(cuò)誤處理(try?)(一般使用于不需要對(duì)錯(cuò)誤進(jìn)行過(guò)多的處理)
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction() // x可能正常返回一個(gè)Int類(lèi)型的值也有可能拋出一個(gè)錯(cuò)誤異常,使用時(shí)對(duì)x用if let可選綁定判斷
十一、可選類(lèi)型在類(lèi)型轉(zhuǎn)換中的運(yùn)用

類(lèi)型轉(zhuǎn)換在向上轉(zhuǎn)型時(shí)一般不會(huì)出現(xiàn)失敗,例如從子類(lèi)向父類(lèi)轉(zhuǎn)型,直接用as即可
類(lèi)型轉(zhuǎn)換在向下轉(zhuǎn)型時(shí)可能會(huì)出現(xiàn)失敗,例如從父類(lèi)向子類(lèi)轉(zhuǎn)型,要使用as?轉(zhuǎn)型,使用時(shí)需要可選綁定后再用

使用as?類(lèi)型轉(zhuǎn)換

值得注意的是:從Any轉(zhuǎn)成Int為向下轉(zhuǎn)型,Swift從安全因素考慮,會(huì)直接返回nil,所以在日常項(xiàng)目中若遇到從父類(lèi)子類(lèi)轉(zhuǎn)型時(shí),一定要使用可選綁定,如以下代碼:

class SuperClass {
    // 這是一個(gè)父類(lèi)
}

class SubClass: SuperClass {
    // 這是一個(gè)子類(lèi)
}

let superClass = SuperClass()
if let someClass = superClass as? SubClass {
    // 如果轉(zhuǎn)換成功,則用someClass使用即可
} else {
    // 轉(zhuǎn)換失敗
}
其他、調(diào)用公共庫(kù)時(shí)注意API的返回值
返回值類(lèi)型
var score: [String: Int] = ["語(yǔ)文": 87, "數(shù)學(xué)": 99, "英語(yǔ)": 61]
let removedValue = score.removeValue(forKey: "物理") // 返回nil

注意:從以上代碼中可看出,removeValue方法會(huì)返回被刪除key對(duì)應(yīng)的Value值,發(fā)現(xiàn)不存在key為“物理”,即返回nil(removedValue常量值)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容