swift 可選型總結

Objective-C與Swift語言對于可選nil的不同理解:

Objective-C中的nil:表示缺少一個合法的對象,是指向不存在對象的指針,對結構體、枚舉等類型不起作用(會返回NSNotFound)
Swift中的nil:表示任意類型的值缺失,是一個確定的值,要么是該類型的一個值要么什么都沒有(即為nil)

一、申明可選常量或變量(非可選型不可以設置為nil,否則會報錯)

let status: Int? = 1   // 申明可選Int類型的常量,初始值為1 
var defaultAddress: String? = "江蘇南京"   // 申明可選String類型的變量,初始值為"江蘇南京"
var student: Person? // 申明可選Person(自定義的類)的變量,初始值為nil

二、使用"!"強制解析獲取可選類型的值(不建議直接使用)

var defaultAddress: String? = "江蘇南京"
if defaultAddress != nil { // !=或==可以用來判斷是否為nil
    print("您的地址是\(defaultAddress!)") // 使用!強制解析
} else {
    print("對不起,您不存在地址信息")
}

var student: Person?
print("學生為\(student!)") // XCode會提示運行錯誤,因為student初始值為nil,強制解析不行

三、使用可選綁定獲取可選類型的值(建議的用法)

var defaultAddress: String? = "江蘇南京"
if let address = defaultAddress { // 如果defaultAddress有值或類型轉換成功,則將值賦值給address直接使用
    print("您的地址是\(address)")  // 使用address代替defaultAddress,且不需要加!強制解析
} else {
    print("對不起,您不存在地址信息")
}

四、隱式解析可選類型(用于申明時肯定有初始值,但后面可能為nil)

var mobileNumber: Int64! = 13912345678 // 第一次申明有初始值
//var mobileNumber: Int64! = nil // 這樣也不會崩潰
print("您的電話號碼是\(mobileNumber)") // 不需要使用!強制解析
// 打印內容:**您的電話號碼是****Optional(13912345678)**
// 還是不建議直接強制解析,因為實際項目中可能中間已經對該值做了改變,若為nil則會運行錯誤導致APP崩潰
if let number = mobileNumber { // 建議的做法
    print("您的電話號碼是\(number)")
// 打印內容:**您的電話號碼是****13912345678**
} else {
    print("您沒有記錄電話號碼")
}

五、空合運算符(用于判斷變量或常量是否為nil)

// 空合運算符:a ?? b 判斷a是否為nil,若a不為nil對a解封,否則返回b的值
var status: Int? // 申明可選Int類型的變量status,初始值為nil
status ?? 0 // 因為status為nil,則返回0
// ?? 即為以下if else的縮寫
func testOption() -> Int {
    let status: Int? = 1
    if status == nil {
        return 0
    } else {
        return status!
    }
}

六、函數/方法返回類型為可選類型

A:返回值為可選類型的值(如Int?、String?、(Int, String)?、[Int]?、[Int: String]?等)
func returnPossibleValue(value: Bool) -> String? { // 返回類型為可選String類型
    if value {
        return "返回類型是可選類型值" // 如果為真,返回Int類型的值1
    } else {
        return nil // 如果為假,返回nil
    }
}

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

func returnPossibleClass(value: Bool) -> SomeClass? { // 返回的類實例可能為nil
    if value {
        return SomeClass(someValue: 1) // 返回的為nil
    } else {
        return SomeClass(someValue: 2) // 返回的SomeClass?實例,不為nil
    }
}
C:返回值為可選類型的閉包(如(()-> (void))? )
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")
}

七、可選類型在類或結構體中的運用

A:可選類型在類中的運用
class PossibleClass {
    var someValue: Int
    var possibleValue: String? // 可選存儲屬性,默認值為nil
    init(someValue: Int) { // 構造方法中可以不對possibleValue屬性初始化
        self.someValue = someValue
    }
}

let someClass = PossibleClass(someValue: 4) // 實例化對象時不需要

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

B:可選類型在結構體中的運用

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

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

八、可選類型在構造器中的運用

概念:可失敗構造器是 一個類、結構體或枚舉類型的對象,在構造過程中有可能失敗;這里所指的“失敗”是指,如給構造器傳入無效的參數值,或缺少某種所需的外部資源,又或是不滿足某種必要的條件等。
A:結構體的可失敗構造器
struct PossibleStructInit {
    let someValue: String
    init?(someValue: String) { // 可失敗構造器
        if someValue.isEmpty { return nil } // 如果實例化為空串,則返回nil(即實例化失敗)
        self.someValue = someValue
    }
}

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

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

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

let twoEnmu = PossibleEnmuInit(director: "A")  // 傳值A,返回PossibleEnmuInit?可選類型
if let two = twoEnmu { // 使用可選綁定判斷
    print(two)
} else {
    print("none value")
}
// 結果打印 none value
C:類的可失敗構造器
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 } // 實例化時如果quantity小于1,則立即終止構造過程
       self.quantity = quantity
       super.init(name: name) // 如果name為空串,則立即終止構造過程
   }
}

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”

九、可選類型在可選鏈中的運用

概念:可選鏈為一種可以在當前值可能為nil的可選值上請求和調用屬性、方法及下標的方法。如果可選值有值,那么調用就會成功;如果可選值是nil,那么調用將返回nil。多個調用可以連接在一起形成一個調用鏈,如果其中任何一個節點為nil,整個調用鏈都會失敗,即返回nil。
class Person { 
    var name: String
    var room: Room? // 不是每個人都買房,定義為可選類型
    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") // 此時xiaoming實例中屬性:name為"xiaoming",room為nil
if let address = xiaoming.room?.roomAddr, let area = xiaoming.room?.roomArea { // 使用?可選鏈式調用,如果含有值則返回該值(不需要手動強制解析),如果沒有值則返回nil
    print("\(xiaoming.name)的房子地址在:\(address), 面積:\(area)")
} else {
    print("\(xiaoming.name)沒有房子")
}
// 打印:xiaoming沒有房子

xiaoming.room = Room(roomAddr: "南京雨花臺區", roomArea: 95) // 定義xiaoming實例的room屬性
// 再次調用以上可選綁定,打印 xiaoming的房子地址在:南京雨花臺區, 面積:95

十、可選類型在錯誤處理中的運用

概念:錯誤處理即為響應錯誤一級從錯誤中恢復的過程

A:一般的錯誤處理寫法(一般使用于需要對不同的返回結果較為清晰的捕捉并做相應的處理)
// 操作錯誤處理枚舉
enum OperationError: Error {
    case ErrorOne
    case ErrorTwo(String) // 帶關聯值的枚舉屬性
    case ErrorOthers
}

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

do {
    print("使用do-catch捕獲錯誤")
    try throwDriver(num: 2) // 使用try嘗試請求
    print("未捕獲到錯誤")
} catch OperationError.ErrorOne { // catch去捕捉是否又throw出來的錯誤
    print("捕捉到錯誤:ErrorOne")
} catch OperationError.ErrorTwo(let message) { // 可以使用let 獲取枚舉關聯值
    print("捕捉到錯誤:ErrorTwo" + message)
} catch OperationError.ErrorOthers {
    print("捕捉到錯誤:ErrorOthers")
}
// 打印結果如下:
// 使用do-catch捕獲錯誤
// 捕捉到錯誤:ErrorTwo數據類型錯誤
B:錯誤處理(try!)(不建議使用,可能會導致App崩潰)
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
// 上述語句中在執行loadImage方法時如果執行失敗,使用try!來禁用錯誤傳遞,會有運行錯誤導致App崩潰
C:錯誤處理(try?)(一般使用于不需要對錯誤進行過多的處理)
func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction() // x可能正常返回一個Int類型的值也有可能拋出一個錯誤異常,使用時對x用if let可選綁定判斷

十一、可選類型在類型轉換中的運用

類型轉換在向上轉型時一般不會出現失敗,例如從子類向父類轉型,直接用as即可

類型轉換在向下轉型時可能會出現失敗,例如從父類向子類轉型,要使用as?轉型,使用時需要可選綁定后再用值得注意的是:從Any轉成Int為向下轉型,Swift從安全因素考慮,會直接返回nil,所以在日常項目中若遇到從父類向子類轉型時,一定要使用可選綁定,如以下代碼:

class SuperClass {
    // 這是一個父類
}

class SubClass: SuperClass {
    // 這是一個子類
}

let superClass = SuperClass()
if let someClass = superClass as? SubClass {
    // 如果轉換成功,則用someClass使用即可
} else {
    // 轉換失敗
}

參考:http://www.lxweimin.com/p/f2e25f78f5a2

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容