Optional可選型
1.什么是可選型
2.為什么要有可選型
3.可選型的使用
4.可選型的本質(zhì)
5.?與!與??
什么是Optional
可選型(Optional)是Swift中的一種類型,或者稱之為一種類型的類別,用來指定某個實例可能沒有值,即要么有值并且可用,要么沒有值,即為nil.
- 在swift中,只有一個實例對象是可選型才可以作為空值(nil),或者對其置空.
- 任何一種類型都可以作為可選型,甚至是元組或者可選型本身.
- swift不允許使用未進行初始化的變量,所以變量都沒有默認值,而只有可選型例外,它的默認值為nil
為什么要有可選型
程序開發(fā)中,多數(shù)語言都有nil的出現(xiàn),導(dǎo)致程序異常退出.所以我們經(jīng)常會做各種各樣的判斷,來避免對一個空值進行非法訪問.
在C語言中,經(jīng)常用-1來表示一個操作失敗,Objective-c中查找字符串的內(nèi)容使用了NSNotFound(本質(zhì)是一個最大的數(shù))來表示未查找到.稱這些值為一個哨兵也不為過(Swift進階).
舉例說明:
NSString *string = nil;
if( [string rangeOfString:@"a"].location != NSNotFound){
NSLog(@"contain");
}else{
NSLog(@"not contain");
}
執(zhí)行打印結(jié)果為"contain"
因為在Objective-c中,我們對一個nil發(fā)送消息,是安全的什么也不會發(fā)生.如果消息返回一個對象,那么nil會被返回,如果是數(shù)值類型,那么返回0,如果是結(jié)構(gòu)體,那么它的值都會被初始化為0.所以哨崗值很容易導(dǎo)致無法預(yù)期的錯誤.
Swift號稱是更加安全的語言,當(dāng)然一部分來自于可選型的加入,一個實例沒有被聲明為可空類型,它就不可能是nil;通過這種方式啦鸣,編譯器知道一個實例是否可能為nil 。這種顯式聲明可以讓代碼更具表達能力蟹漓,也更安全.
可選型的使用
聲明一個可選型使用?或者!緊跟類型后面,不可以有空格
var option:Int? = "abc"
option = nil
!表示為隱式可選型,在后面會進行敘述.
使用可選型的值我們需要對其進行解包,解開的方式也有很多種
- if判斷
- 隱式解包
- 強制解包
- 可選綁定
- ??空合運算符
//1.
if option != nil{
//do something
}
//2.?
//如果值為nil將什么也不發(fā)生
option?var
//3. !當(dāng)為nil,會導(dǎo)致程序異常退出
option!
可選綁定
optional binding是一種固定模式攀涵,對于判斷可空實例是否有值很有用访敌。如果有值,就將其賦給一個臨時常量或變量弧岳,并且使這個常量或變量在條件語句的第一個分支代碼中可用轿曙。這樣可以讓代碼更簡潔,同時保持表達力韵丑。
if let value = optional{
//有值 do some
}else{
//沒有值nil
}
當(dāng)然可選實例綁定可以進行嵌套,當(dāng)遇到多個可選型,嵌套可空實例綁定可能會顯得錯綜復(fù)雜。就像是金字塔一樣,所以單個if let綁定就可以展開多個可空實例.
可空鏈式調(diào)用 (optional chaining)提供了一種對可空實例進行查詢以判斷其是否包含值的機制虚缎。兩者的一個重要區(qū)別是撵彻,可空鏈式調(diào)用允許程序員把多個查詢串聯(lián)為一個可空實例的值。如果鏈式調(diào)用中的每個可空實例都包含值实牡,那么每個調(diào)用都會成功陌僵,整個查詢鏈會返回期望類型的可空實例
if let error = errorStr{
if let code = Int(error){
//......
}
}
//更優(yōu)雅
if let error = errorStr,code = Int(error){
//.....
}
當(dāng)然也可以使用guard,while(不常用).guard語句的Optional綁定與if語句類似,區(qū)別有兩個创坞,一個就是guard語句與if語句本身的語義區(qū)別碗短;還有一個是guard語句中聲明的對象是處于guard語句所在的作用域的,而不僅限于guard語句塊作用域题涨。所以guard語句的Optional綁定在實際項目中可能會用得更多些偎谁,因為它更加便利「俣拢可將其聲明的拆解掉Optional的對象將之前所聲明的Optional對象標識給完全覆蓋掉巡雨。
var a = Int?(0)
//覆蓋同名變量
guard let a = a else{
return
}
a += 10
可選型的本質(zhì)
可選型實際是一個泛型枚舉,遵循ExpressibleByNilLiteral協(xié)議(nil字面量),我們可以顯示的聲明一個Optional.
var website: Optional<String> = Optional.some("www.baidu.com")
print(website)
website = Optional.none
print(website)
當(dāng)然也可以點擊查看其聲明
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
.....
}
?與!與??
?
?語法糖其實就是簡化了聲明和賦值
var optional:Optional<Int>
//等同于var optional:Int?
//賦值時 等同于optional = nil
optional = Optional<Int>.none
optional = Optional<Int>.some(5)
//等同于optional = 5
在聲明或者賦值一個Optional的變量時,?語法糖做的事情就是聲明一個Optional<T>席函,然后查看等號右邊是不是nil這個標記值铐望。如果不是,則使用 init(_ some: T)用等號右邊的類型T的值生成一個 .some枚舉并賦值給這個Optional 變量,如果是nil茂附,將其賦為none枚舉正蛙。
? 放在某個Optional 變量后面,表示對這個變量進行判斷营曼,并且隱式地unwrap
optional?.distance(to: 10)
//相當(dāng)于
if let value = optional{
value.distance(to: 10)
}
更存在價值的地方在于可以鏈式調(diào)用乒验,也就是所謂的 Optional Chaining,這樣可以避免一大堆的條件分支蒂阱,而使代碼變得易讀簡潔徊件。
? 放在某個 optional 的 protocol (Objective-C)方法的括號前面奸攻,以表示詢問是否可以對該方法調(diào)用
!
! 放在Optional變量的后面,表示強制的 unwrap 轉(zhuǎn)換:
使一個 Optional<T> 的量被轉(zhuǎn)換為 T虱痕。特別注意睹耐,如果這個 Optional 的量是 nil 的話,這種轉(zhuǎn)換會在運行時讓程序崩潰.
func !<T>(value:Optional<T>) -> T{
switch value{
case .none:
fatalError("error:nil")
case .some(v):
return v
}
}
!放在類型后面,隱式解析可選類型,表示強制的隱式轉(zhuǎn)換部翘。是一個類型聲明的語法糖硝训。?聲明的是Optional,而!其實聲明的是一個ImplicitlyUnwrappedOptional(deprecated)類型新思。會在使用的時候自動地去unwrap窖梁,繼續(xù)之后的操作調(diào)用,而不必去增加一次手動的顯示/隱式操作夹囚。隱式解析可選類型依然是一個可選類型纵刘,可選類型的特性它同樣適用,只是注意荸哟,當(dāng)隱式解析可選項值為 nil 的時候解析取值假哎,會運行出錯。如果一個變量之后可能變成nil的話請不要使用隱式解析可選類型鞍历。如果你需要在變量的生命周期中判斷是否是nil的話舵抹,請使用普通可選類型。
??nil-coalescing operation 空和運算符
??提供一個默認值,當(dāng)一個可選型為nil時,使用該值,否則就是其解包后的值
var optional:Int? = nil
optional ?? 5
覺得似曾相識,跟三目運算符差不多,用三目運算符可以對其改寫即optional != nil ? optional! : 5,那么他和三目運算一樣嗎?肯定不一樣,如果?前面后面是一個比較耗時繁瑣的操作,那么效率就會很差.
我們可以在可選型的定義中查看該操作符的定義.
public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T
至于這里為什么使用了自動閉包,我們可以大致實現(xiàn)其邏輯,使用了閉包我們就可以達到短路的效果,當(dāng)值不是nil時,避免不必要的計算.
public func ??<T>(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T{
if let value = optional{
return value
}else{
return defaultValue()
}
}
??用于聲明類型或者更多個???....
當(dāng)聲明一個變量做類型標注時可能會遇到多個??,其實就是嵌套可選型,需要我們進行多次解包,就好像盒子里面又是一個盒子..
let number: Int??? = 10
print(number)
// Optional(Optional(Optional(10)))
解包也有多種方式
let number: Int??? = 10
//暴力拆解型
print(number!!!)
//嵌套地獄型
if let number = number {
if let number = number {
if let number = number {
print(number)
}
}
}
//可選綁定
if let n1 = number,
let n2 = n1,
let n3 = n2 {
print(n3)
}
func printNumber(_ number: Int???) {
guard let n1 = number, let n2 = n1, let n3 = n2 else {
return
}
print(n3)
}