一起养成写作习惯!这是我参与「掘金日新计划 · 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"