相信大家在學(xué)習(xí)和使用Swift的時(shí)候,肯定會(huì)被 ! 和 ? 搞瘋過(guò), 糾結(jié)這兩個(gè)符號(hào)到底是個(gè)什么鬼 ?鬼知道什么時(shí)候使用!,什么時(shí)候使用?
下面就說(shuō)一下! 和 ? 區(qū)別以及該怎么使用!
? 和 ! 到底是個(gè)啥
? 和 ! 其實(shí)分別是Swift語(yǔ)言中對(duì)一種可選類(lèi)型( Optional) 操作的語(yǔ)法糖。 那可選類(lèi)型是干什么的呢? Swift中是可以聲明一個(gè)沒(méi)有初始值的屬性, Swift中引入了可選類(lèi)型(Optional)來(lái)解決這一問(wèn)題。它的定義是通過(guò)在類(lèi)型生命后加加一個(gè) ? 操作符完成的。
例如: var name: String?
Optional其實(shí)是個(gè)enum,里面有None和Some兩種類(lèi)型。其實(shí)所謂的nil就是Optional.None , 非nil就是Optional.Some, 然后會(huì)通過(guò)Some(T)包裝(wrap)原始值,這也是為什么在使用Optional的時(shí)候要拆包(從enum里取出來(lái)原始值)的原因。
這里是enum Optional的定義
enum Optional<T> : LogicValue, Reflectable { case None case Some(T) init() init(_ some: T) /// Allow use in a Boolean context. func getLogicValue() -> Bool /// Haskell's fmap, which was mis-named func map<U>(f: (T) -> U) -> U? func getMirror() -> Mirror }既然這樣, 那對(duì)于 var name: String? 該怎樣去理解這句語(yǔ)法呢?
var name: String?
// 上面這個(gè)Optional的聲明,是”我聲明了一個(gè)Optional類(lèi)型值,它可能包含一個(gè)String值,也可能什么都不包含”,也就是說(shuō)實(shí)際上我們聲明的是Optional類(lèi)型,而不是聲明了一個(gè)String類(lèi)型 (這其實(shí)理解起來(lái)挺蛋疼的...)
? 和 ! 使用
一旦聲明為Optional的,如果不顯式的賦值就會(huì)有個(gè)默認(rèn)值nil。判斷一個(gè)Optional的值是否有值,可以用if來(lái)判斷:
if name {
// 有值再操作
}
怎么使用Optional值呢?文檔中也有提到說(shuō),在使用Optional值的時(shí)候需要在具體的操作,比如調(diào)用方法、屬性、下標(biāo)索引等前面需要加上一個(gè)?,如果是nil值,也就是Optional.None,會(huì)跳過(guò)后面的操作不執(zhí)行,如果有值,就是Optional.Some,可能就會(huì)拆包(unwrap),然后對(duì)拆包后的值執(zhí)行后面的操作,來(lái)保證執(zhí)行這個(gè)操作的安全性。
// 例如:
let length = name?.characters.count
PS:對(duì)于 Optional 值,不能直接進(jìn)行操作,否則會(huì)報(bào)錯(cuò)。
? 的使用場(chǎng)景:
1.聲明Optional值變量
2.用在對(duì)Optional值操作中,用來(lái)判斷是否能響應(yīng)后面的操作
3.使用 as? 向下轉(zhuǎn)型(Downcast)
上面提到Optional值需要拆包(unwrap)后才能得到原來(lái)值,然后才能對(duì)其操作,那怎么來(lái)拆包呢?
拆包有兩種方法:
可選綁定(Optional Binding)
可選綁定(Optional Binding)是一種更簡(jiǎn)單更推薦的方法來(lái)解包一個(gè)可選類(lèi)型。 使用可選綁定來(lái)檢查可選類(lèi)型的變量有值還是沒(méi)值。如果有值, 解包它并且將值傳遞給一個(gè)常量或者變量。
// 例子最為簡(jiǎn)單明了var str: String? = "Hello"let greeting = "World!"if let name = str { let message = greeting + name print(message)}/**自然語(yǔ)言解釋意思:就是如果str有值,解包它,并且將它的值賦值給name, 然后執(zhí)行下面的條件語(yǔ)句; 如果str為空, 直接跳過(guò)條件語(yǔ)句塊。*/ 硬解包
硬解包即直接在可選類(lèi)型后面加一個(gè)感嘆號(hào)(!)來(lái)表示它肯定有值。
var str1: String? = "Hello"let greeting = "World!"if (str1 != nil) { let message = greeting + str1! print(message)}/**上面例子,我們只是自己知道str1肯定有值, 所以才直接硬解包了str1變量。 但是萬(wàn)一有時(shí)候我們的感覺(jué)是錯(cuò)的, 那程序在運(yùn)行時(shí)可能會(huì)出現(xiàn)嚴(yán)重的錯(cuò)誤. 所以Swift中是推薦先檢查可選類(lèi)型是否有值, 然后再進(jìn)行解包的!*/ 錯(cuò)誤示范:
var str1:String? // str1值可能是傳過(guò)來(lái)的值或者從服務(wù)器獲取的值let msg = "Hi"let txt = msg + str1! // runtime error/** 以上代碼在編譯階段不會(huì)報(bào)錯(cuò).因?yàn)槭褂昧擞步獍? 編譯器認(rèn)為可選類(lèi)型是有值的, 所以編譯是通過(guò)的. 當(dāng)代碼運(yùn)行起來(lái)時(shí), 知名的錯(cuò)誤將會(huì)出現(xiàn): `fatal error: Can't unwrap Optional.None`*
PS:對(duì)于 ! 操作符,這里的變量值一定是非nil的!
其實(shí), 還有一種叫隱式拆包(Implicitly Unwrapped Optionals),比如 對(duì)于會(huì)在viewDidLoad進(jìn)行初始化的變量,可以直接定義為var str :String! 等于說(shuō)你每次對(duì)這種類(lèi)型的值操作時(shí),都會(huì)自動(dòng)在操作前補(bǔ)上一個(gè)!進(jìn)行拆包,然后在執(zhí)行后面的操作,當(dāng)然如果該值是nil,會(huì)報(bào)錯(cuò)crash掉。
舉個(gè)很淺顯的栗子:
// 在一個(gè)viewController里面,從xib里面拖一個(gè)UIImageView控件, 你會(huì)發(fā)現(xiàn)Xcode會(huì)自動(dòng)給你轉(zhuǎn)成下面的形式 @IBOutlet weak var headerBGImageView: UIImageView!/** 聲明Implicitly Unwrapped Optionals值,一般用于類(lèi)中的屬性*/
PS:如果你在隱式解析可選類(lèi)型沒(méi)有值的時(shí)候進(jìn)行取值,會(huì)crash。和在沒(méi)有值的可選類(lèi)型里面拆包是一樣的。
! 的使用場(chǎng)景
1.強(qiáng)制對(duì)Optional值進(jìn)行拆包(unwrap)
2.聲明隱式拆包變量,一般用于類(lèi)中的屬性
結(jié)束
其實(shí)! 和 ? 的問(wèn)題是很坑的,不要看它僅僅是兩個(gè)符號(hào),因?yàn)橹灰幸粋€(gè)不小心,不注意,你會(huì)發(fā)現(xiàn)項(xiàng)目運(yùn)行起來(lái),會(huì)莫名的crash掉了,關(guān)鍵是Debug模式也不是很方便定位錯(cuò)誤類(lèi)型。 自己整理一下關(guān)于 可選類(lèi)型的相關(guān)使用,一是記錄和鞏固所學(xué),而是希望會(huì)對(duì)大家有所幫助。 本文可能會(huì)有錯(cuò)誤和不妥之處,還望提出,我會(huì)及時(shí)改正。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注