swift 可选值简介4

425 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第19天,点击查看活动详情

可选值判等
 let regex = "123"
 if regex.first == "1" {}

上面的代码可以运行基于俩点,Wrapped类型实现了Equatable协议,Optional才会实现Equatable协议

extension Optional: Equatable where Wrapped: Equatable {
    static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
        switch (lhs, rhs) {
        case (nil, nil): return true
        case let(x?, y?): return x == y
        case (nil, _?), (_?, nil): return false
        }
    }
}

我们不一定需要写这样的代码

if regex.first == Optional("1") {}

当我们使用非可选值时候,如果需要匹配成可选值类型,Swift总会将他“升级”一个可选值类型

如果没有这个隐式转换,你需要实现是三个分开版本

// 两者都可选
func == <T: Equatable>(lhs: T?, rhs: T?) -> Bool
// lhs 非可选
func == <T: Equatable>(lhs: T, rhs: T?) -> Bool
// rhs 非可选
func == <T: Equatable>(lhs: T?, rhs: T) -> Bool

实际上,我们就只需要第一个版本。很多时候都依赖这个隐式转换,比如可选map时,我们将内部值进行转换并且返回。但是,我们的map返回值其实是个可选值,编译器自动帮我们完成转换,我们不需要写return Optional(transform(value))这样的代码。在字典中因为键可能不存在,所以返回值为可选值,如果没有隐式转换我们需要写myDict["key"] = Optional(someValue)的代码

如果想为字典赋值为nil

var dictWithNils: [String: Int?] = ["one": 1,
                                    "two": 2,
                                    "three": nil]

dictWithNils["two"] = nil 会把键移除

可以有以下做法

dictWithNils["two"] = .some(nil)
dictWithNils["two"] = Optional(nil)
dictWithNils["two"]? = nil

第三种方式使用了可选链的方式来获取成功后对值进行设置

对于不存在的值并没有值被更新或者插入

dictWithNils["four"]? = nil
print(dictWithNils)  // ["one": Optional(1), "two": Optional(2), "three": nil]

强制解包的时机

当你确定某个值不可能是nil时可以用感叹号,你应当希望如果它是!,程序应当直接挂掉

extension Sequence {
    func alt_commpactMap<T>(_ transform: (Element) -> T?) -> [T] {
        return lazy.map(transform).filter { $0 != nil }.map { $0! }
    }
}

这里filter已经把nil给过滤出去,所以map使用!完全没有问题

改进强制解包的错误信息
infix operator !!
func !!<T>(wrapped: T?, failtureText:@autoclosure () -> String) -> T {
    if let x = wrapped { return x }
    fatalError(failtureText())
}
let s = "foo"
let i = Int(s) !! "Expecting integer, got"(s)""
在调试版本中使用断言

使用assert在进行断言,发布版本将替换为默认值

infix operator !?
func !?<T: ExpressibleByNilLiteral>(wrapped: T?, failureText: @autoclosure () -> String) -> T {
    assert(wrapped != nil, failureText())
    return wrapped ?? 0 
}

如果需要提供一个显式的默认值,可以定义一个接受元组的参数,包含默认值和错误信息

infix operator !?
func !?<T: ExpressibleByNilLiteral>(wrapped: T?, nilDefault: @autoclosure () -> (value: T, text: String)) -> T {
    assert(wrapped != nil, nilDefault().text)
    return wrapped ?? nilDefault().value
}
// 调试版本中断言,发布版本中返回 5
Int(s) !? (5, "Expected integer")

对于返回Void的函数,使用可选链进行调用会返回Void?, 可以写一个非泛型的版本检测一个可选链调用碰到nil,且无操作的情况

infix operator !?
func !?(wrapped: ()?, failureText: @autoclosure () -> String) {
    assert(wrapped != nil, failureText())
}
var output: String? = nil
output?.write("something") !? "wasn't expecting chained nil here"