Swift函数式编程四(可选值)

Posted 酒茶白开水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift函数式编程四(可选值)相关的知识,希望对你有一定的参考价值。

Swift的可选类型可以用来表示可能缺失或是计算失败的值。

案例:字典

无法保证字典查询操作总是返回一个值,Swift可选类型可以表示这种失败的可能性:

let citys = ["Paris": 2241, "Madrid": 3165, "Amsterdam": 827, "Berlin": 3562]
let madridPopulation: Int? = citys["Madrid"]

Swift可选类型解包方式:

  • 可选绑定
  • 使用!强制解包
  • 隐式解包
  • 使用??提供一个默认值

// 强制解包
print(madridPopulation!)

// 隐式解包
let v: Int! = madridPopulation
print(v*10)

// ?? 运算符提供默认值
let v1 = madridPopulation ?? 100
print(v1)

没有检验的强制解包和隐式解包都是糟糕的代码意味,预示着可能发生运行时错误。所以建议使用可选绑定和??运算符。

使用??运算符需要提供一个默认值,当结果为nil时,这个默认值将作为返回值。简单说,它可以如下定义:

// 定义运算符
infix operator ???
func ???<T>(optional: T?, defaultValue: T) -> T 
    if let result = optional 
        return result
     else 
        return defaultValue
    

上面的定义有一个问题,如果默认值是通过某个函数或表达式得到的,那么无论可选值是否为nil,defaultValue都会求值。所以应该只在可选参数为nil时才对defaultValue参数求值,可以按如下方式解决这个问题:

infix operator ????
func ????<T>(optional: T?, defaultValue: () -> T) -> T 
    if let result = optional 
        return result
     else 
        return defaultValue()
    


let v3 = madridPopulation ????  100 
print(v3)

美中不足的是现在的defaultValue参数是一个显式闭包,使用Swift的autoclosure类型标签可以避开创建显式闭包:

infix operator ?????: AdditionPrecedence
func ?????<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T 
    if let result = optional 
        return result
     else 
        return defaultValue()
    


let v4 = madridPopulation ????? 100
print(v4)

可选值链

Swift有一个特殊的机制,可选值链。用于被嵌套的类、结构体中对方法和函数进行选择。考虑如下客户订单模型代码片段:

struct Address 
    let city: String
    let state: String?

struct Person 
    let name: String
    let address: Address?

struct Order 
    let orderNumber: String
    let person: Person?

let order = Order(orderNumber: "123456789", person: Person(name: "张三", address: Address(city: "北京", state: "长安街")))

给定一个order,如何获取订单的用户所在的街道信息?

  • 使用强制解包非常不安全(极易引起运行时异常)
  • 使用可选绑定相对更安全但是显得麻烦
  • 使用可选链(一个组成项为nil时,整个语句链返回nil)就更加安全简洁
// 强制解包
print(order.person!.address!.state!)

// 可选绑定
if let person = order.person 
    if let address = person.address 
        if let state = address.state 
            print(state)
        
    


// 可选连解包
if let state = order.person?.address?.state 
    print(state)

分支上的可选值

除了if let可选绑定,switchguard也非常适合与可选值搭配使用。

switch语句中匹配可选值,可以简单地为case分支的每个模式添加一个?后缀,如果对特定值没有兴趣,也可以匹配OptionalSome值和None值:

// switch语句匹配可选值
let options = [0, 1, 10, 100, nil]
for i in options 
    switch i 
    case 0?:
        print("我是零")
    case (1..<100)?:
        print("一百以内的数")
    case .some(let x):
        print(x)
    case .none:
        print("没有值呀")
    

guard语句的设计旨在当条件不满足时,可以尽早退出当前作用域,语句后面的代码需要值存在才能执行,这是一个很常用的情境,让控制流比使用if let语句更简单:

// guard 语句
func populationDescriptionForCity(city: String) -> String? 
    guard let population = citys[city] else 
        return nil
    
    
    return "\\(city)的人口是\\(population)万"

print(populationDescriptionForCity(city: "Paris") ?? "")

可选映射

?运算符允许我们选择性的访问可选值的方法或字段,在实际应用中往往都是如果有值就操作它否则返回nil,如下:

func incrementOptional(optional: Int?) -> Int? 
    guard let result = optional else 
        return nil
    
    
    return result + 1

还可以将对可选值的任何运算作为参数传递给map函数,这个函数是Swift标准库的一部分:

extension Optional 
    func map<U>(transform: (Wrapped) -> U) -> U? 
        guard let result = self else 
            return nil
        
        return transform(result)
    

可选绑定

map函数展示了一种操作可选值的方法,但还有很多其它方法。

如何计算如下两个可选值的和:

let x: Int? = 3
let y: Int? = nil

由于Int?不支持+运算符,可以使用if letguard let

func addOptionals(optionalX: Int?, optionalY: Int?) -> Int? 
    if let xV = optionalX 
        if let yV = optionalY 
            return xV + yV
        
    
    
    return nil


func addOptionals1(optionalX: Int?, optionalY: Int?) -> Int? 
    guard let xV = optionalX, let yV = optionalY else 
        return nil
    
    
    return xV + yV

还有一种途径能解决上述问题,那就是借助Swift标准库中的flatMap函数。很多类型中都定义了flatMap函数,可选类型的flatMap函数定义如下:

extension Optional 
    func flatMap<U>(transform: (Wrapped) -> U?) -> U? 
        guard let v = self else 
            return nil
        
        
        return transform(v)
    

使用flatMap函数来重写两个可选值相加:

func addOptionals2(optionalX: Int?, optionalY: Int?) -> Int? 
    optionalX.flatMap  (xV) -> Int? in
        optionalY.flatMap  (yV) -> Int? in
            xV + yV
        
    

为何使用可选值

Swift使用可选类型增强静态安全,能在代码执行前捕获到错误,有助于避免缺失值导致意外奔溃。

Objective-C中不能区分字典中key存在value不存在和key不存在的情况,除非使用NSNull。

虽然在Objective-C中对nil发送消息是安全的,但是使用nil往往不安全。例如使用nil初始化NSAttributedString会奔溃。

可选类型有助于捕捉一些难以察觉的细微错误,例如Objective-C中对nil调用rangeOfString方法会返回一个属性全为0的结构体,然而NSNotFound被定义为NSIntegerMax。

坚持使用可选值能够从根本上杜绝这类错误。

以上是关于Swift函数式编程四(可选值)的主要内容,如果未能解决你的问题,请参考以下文章

《函数式swift》—swift为什么是用可选值?

Swift:错误:在隐式展开可选值时意外发现 nil

在隐式展开可选值 AVAUDIO Player SWIFT 时意外发现 nil

Swift Xcode 致命错误:在隐式展开可选值时意外发现 nil [重复]

Swift:XCTest 会修改状态吗?可以返回可选值的测试函数的约定是啥?

Swift:在展开可选值时发现映射函数为零