可選值
4.1哨崗值 Sentinel Value
定義:有的函式可能返回nil 當函式返回nil 我們將返回值 用作-1 等類似的值代替,這樣的值就被稱為"哨崗值" 也叫魔法數。
4.2通過列舉來解決魔法數的問題
swift中Optional的本質是一個列舉(目前可以先不具體思考,等以後的專案中慢慢就領悟到了)。
enum Optional<Wrapped> {
case none //為空
case some(wrapped) // 有值
}
複製程式碼
知識點:當返回值為可選值 ? 時,實質上返回的是一個Optional的列舉
///返回值為Optional的實質
let strArray = ["one","two","three"]
switch strArray.index(of: "four") {
case .none:
print("返回值為空") //返回值為空
case .some(let idx):
print(idx)
print("有值")
}
複製程式碼
4.3可選值概覽:Optional
if let
相信大家在swift 入門的時候就已經能很好的使用這個語法了。 不贅述。
技巧1: 可與Bool
聯合判斷
if let obj = Optional , Bool {
/// 前面有值且後面Bool為true執行
}
複製程式碼
技巧2: 多let 並列
。 這個在開發中也經常用到
if let obj1 = Optional1, let obj2 = Optional2, ....{
///當上面的let判斷都有值的時候執行
}
複製程式碼
while let
類似於if let 遇到nil時終止迴圈
也有和上面?技巧1(一旦為false 就迴圈停止,並不是filter那樣去工作
)相同的使用方法。不贅述。
while let line = printLine() {
///當printLine為空時候終止迴圈
print(line)
}
複製程式碼
雙重可選值:
一個可選值可能會被另外一個可選值包裝起來,形成了可選值的巢狀。你也可以理解成obj?? 。平時在複雜邏輯操作時注意一哈就可以了。
if var 和 while var
和 let 的使用類似。 但你var 出來的obj實際上是一個本地副本
你對obj做任何更改都不會影響
原來的值。
///if var 的改變不影響原值
let dict:[String:String]?
dict = ["key1":"value1"]
if var varDict = dict {
varDict["key1"] = "value2"
}
print(dict) //Optional(["key1": "value1"])
複製程式碼
解包後可選值的作用域
if let obj = Optional {
///obj的作用域
}
複製程式碼
注:有時候我們需要提前退出
來避免煩人的if巢狀,這個時候 guard let
的優點馬上就體現出來了.
///為空提前退出
guard let obj = Optional else{return}
複製程式碼
可選鏈
OC中對nil傳送訊息,nothing had happened。 在swift中可以通過可選鏈(Optional chaining)達到一樣的效果
那麼問題來了,什麼是可選鏈?
可選鏈
中的可選是?
,可以把鏈理解成swift中的點語法,可以在《swift鏈式程式設計》一書中對這個有更深的理解
delegate?.callback() //就是一個可選鏈。
//當上面的delegate為可選時編譯器會發出警告。 確實有值方法會被呼叫。沒有值時候方法不會被呼叫。
複製程式碼
ps. ?寫了這麼多年swift的delegate和閉包 直到自己寫筆記的時候才發現自己寫複雜了(一點swift的優點都沒體現出來。。。)
var voidCallback:(()-> Void)?
///對於閉包呼叫的兩種寫法
//❌不推薦
if voidCallback != nil {
voidCallback!()
}
//✅ 推薦
voidCallvack?()
複製程式碼
多個可選鏈的呼叫。下面這個例子簡單易懂,不贅述。
extension Int {
var half: Int? {
guard self > 1 else {return nil}
return self/2
}
}
///多可選鏈呼叫
20.half?.half?.half //Optional(2)
複製程式碼
nil合併運算子 ?? (專案中經常會用到!!可以讓你的程式碼寫的很優美~)
一開始看到標題nil合併運算子
的時候我是?的。這都是啥啥啥啊。 當我看到 ??
這個符號時候豁然開朗。兩個是一個意思 專案裡面已經用過很多次了,百試不爽。
使用場景
: 當去解包!
一個可選值,且當這個可選值為nil時,我們要去賦一個預設值
防止崩潰 這個時候就用到了 ??
var number: Int?
number = nil
String(number ?? 5) //5
複製程式碼
感覺上是和OC 中的 三目運算子 Bool ? A: B 類似
需要強調的是可選值不是指標
可選值map
可選值flatMap(Demo值得借鑑)
上面兩小節講的都是map 和flatMap對於可選值的一個用法。
有一個小demo對於日常開發可以說是有著四兩撥千斤
的感覺,如下:
///將一個網路圖片載入出來的奇淫方法!!!
let urlString = "http://www.objc.io/logo.png"
let view = URL(string: urlString)
.flatMap { (url) -> Data? in
try? Data(contentsOf: url)
}
.flatMap { (data) -> UIImage? in
UIImage(data: data)
}
.map { (image) -> UIImageView in
UIImageView(image: image)
}
if let view = view {
UIView().addSubview(view)
}
複製程式碼
利用swift中的$0
語法簡化上面的程式碼
let view2 = URL(string: urlString)
.flatMap {
try? Data(contentsOf: $0)
}
.flatMap {
UIImage(data: $0)
}
.map {
UIImageView(image: $0)
}
if let view2 = view2 {
UIView().addSubview(view2)
}
複製程式碼
這裡的flatMap
和if let
非常相似。
這樣我們就將一個網路圖片載入出來了。
想想自己之前寫了那麼多的垃圾程式碼,不禁長嘆一句:swift??!!!
使用flatMap過濾nil
同樣這裡我們用一個demo去說明用法
目的
:我們想要求一個字串陣列中的數字和。
let numbers = ["1","2","3","4","liaoworking"]
///普通青年
var sum = 0
for case let i? in numbers.map({
Int($0)
}) {
sum += i// Int($0)為nil就不走這裡了
}
// sum的值為10
///優質青年
///當我們用?? 把nil替換成0
numbers.map { Int($0) }.reduce(0) { $0 + ($1 ?? 0)} //10
///文藝青年
///在標準庫中flatMap的作用可能正是你想要
numbers.flatMap { Int($0) }.reduce(0, +) // 10
複製程式碼
注:這裡的flatMap的作用:把一個對映為可選值的序列進行展平。
這個例子的確好吊,不自己寫一遍不知道還有很多高階用法自己不會。
學習了學習了~
如果讓我去面試swift開發。一定會選這個demo做一個面試題!
可選值判等
判等時我們並不關心可選值是不是nil,只用去管它是否包含
我們想要的非nil的特定值
即可。
switch case 匹配可選值
/// switch case
let numbers = ["1","2","3","4","liaoworking"]
for i in numbers {
switch Int(i) {
case 0:
print("0")
case nil:
print("can't convert to int")
default:
print("not in case")
}
}
複製程式碼
swift中case匹配是通過~=運算子進行的。 我們可以實現這個方法增加對範圍
(0..<num)匹配的方法
###可選值的比較
和 == 類似 可選值曾經也有 <=, >=, <, >操作符 後來在SE-0121因為安全原因
被移除了。
///在一個溫度陣列中
let temp = ["-23", "0", "66.66", "warm"]
Double("warm") 的值為nil
Double("warm") < 0 是成立的
但你不能說warm是小於0度的是很冷的。
複製程式碼
這顯然不合情理。