一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
通过枚举解决魔法数问题
enum Optional<Wrapped> {
some(Wrapped)
none
}
获取枚举关联值的唯一方式就是通过模式匹配, 就像switch或if case let 中使用的匹配方法一样。和哨岗值不同,除非你式并检查解包,否则不能获取Optional中包装的值。
哨岗值:返回了一个“魔法”数来表示其并没有返回真实的值。这样的值被称为“哨岗值 (sentinel values)
extension Collection where Element: Equatable {
func firstIndex(of element: Element) -> Optional<Index> {
var idx = startIndex
while idx != endIndex {
if self[idx] == element {
return .some(idx)
}
formIndex(after: &idx)
}
return .none
}
}
返回的是Optional类型,非哨岗值
- Optional可以写成Index?
- 可选值遵循ExpressibleByNilLiteral协议,所以可以用nil表示 .none
- return .some(idx) 就可以直接写为 return idx
- 可选值就是一个普通的枚举值,完全可以自定义
let stringNumbers = ["1", "2", "three"]
let idx = stringNumbers.firstIndex(of: "1")
// 编译错误,remove(at:)接收Int,非Optional<Int>
stringNumbers.remove(at: idx)
必须解包
var stringNumbers = ["1", "2", "three"]
switch stringNumbers.firstIndex(of: "1") {
case .some(let idx):
stringNumbers.remove(at: idx)
break
case .none:
break
}
更简明的写法:
var stringNumbers = ["1", "2", "three"]
switch stringNumbers.firstIndex(of: "1") {
case let idx?:
stringNumbers.remove(at: idx)
break
case nil:
break
}
可选值概览
if let
var array = ["one", "two", "three", "four"]
if let idx = array.firstIndex(of: "four") {
array.remove(at: idx)
}
if let idx = array.firstIndex(of: "four"), idx != array.startIndex {
array.remove(at: idx)
}
let urlString = "https://www.objc.io/logo.png"
if let url = URL(string: urlString),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data)
{
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view
}
if let url = URL(string: urlString), url.pathExtension == "png",
let data = try? Data(contentsOf: url),
let image = UIImage(data: data)
{
let view = UIImageView(image: image)
}
while let
当一个条件返回nil时便终止循环
while let line = readLine() {
print(line)
}
while let line = readLine(), !line.isEmpty {
print(line)
}
- next()遍历
let array = [1, 2, 3]
var iterator = array.makeIterator()
while let i = iterator.next() {
print(i, terminator: " ")
}
- for..in + where
for i in 0..<10 where i % 2 == 0 {
print(i, terminator: " ")
} // 0 2 4 6 8
while+迭代器重写
var iterator2 = (0..<10).makeIterator()
while let i = iterator2.next() {
guard i % 2 == 0 else { continue }
print(i)
}
双重可选值
let stringNumbers = ["1", "2", "three"]
let maybeInts = stringNumbers.map { Int($0) }
for maybeInt in maybeInts {
// maybeInt 是一个 Int? 值
// 得到两个整数值和一个 `nil`
}
for...in 是 while 循环加上一个迭代器的简写方式。由于 next 方法会把序列中的每个元素包装成可选Optional<Optional>值,或者说是一个Int??,而while let 会解包检查这个值是不是nil,如果不是,则绑定解包的值并运行循环体部分
var iterator = maybeInts.makeIterator()
while let maybeInt = iterator.next() {
print(maybeInt, terminator: "")
}
当循环到"three"转换而来nil.从next()返回的是一个非nil的值,这个值是.some(nil).while let 将这个值解包,并将解包结果(也就是nil)绑定到maybeInt上。
如果只想对非nil的值做for循环,可以用case来做模式匹配
for case let i? in maybeInts {
// i 将是 Int 值,而不是 Int?”
print(i, terminator: "")
}
// 或者只对 nil 值进行循环
for case nil in maybeInts {
// 将对每个 nil 执行一次
print("No value")
}”
x?这个模式只会匹配非nil值。这个是.some(x)简写
for case let .some(i) in maybeInts {
print(i)
}
基于case的匹配模式可以让我们在switch的匹配中用到的规则应用到for, if和while上去.最有用的场景是结合可选值,但也有其它一些使用方式
let j = 5
if case 0..<10 = j {
print("(j) 在范围内")
} // 5 在范围内”