Swift - 使用Optional的flatMap来让nil向下传递

背景

当我知道Optional也有flatMap方法的时候我的内心是很惊讶的. 之前CollectionflatMap就因为两个重载方法搞得我晕了一阵.而且因为不常用对所以对flatMap一直没有一个概念. 最近一些对可选值的使用场景让我调研并使用了一下, 结合唐巧的一篇博客, 算是对flatMap这个关键词有了统一的认识.

使用场景

假设我们要设计一个函数, 函数参数是一个image的data, 我们要把它先转换为UIImage, 然后将其转换为jpg格式,压缩0.3倍, 然后返回压缩后的UIImage. 常规的话我们可能会这么写.

func getCompressImage(by data: Data?) -> UIImage? {
        guard let data = data,
          let image = UIImage(data: data),
          let jpegImageData = image.jpegData(compressionQuality: 0.3),
          let compressImage = UIImage(data: jpegImageData) else {
            return nil
        }
        return compressImage
    }

这样其实已经算是比较清晰了(比起if let)

如果使用flatMap会是这样的

func getCompressImage(by data: Data?) -> UIImage? {
     return data.flatMap {
                    UIImage(data: $0)
                }.flatMap{
                    $0.jpegData(compressionQuality: 0.3)
                }.flatMap{
                    UIImage(data: $0)
                }
    }

flatMap函数的核心思想是对一个容器的元素进行再变形,变形为新的容器, 这里的容器就是指Optional, 所以我们可以看到它可以将异常情况nil进行下沉. 在一步步的操作中我们并没有进行解包操作, 只是将有值得情况进行处理, nil的情况向下传递下去. 代码其实更清晰简洁了.
并且不是类似的情况都能用guard let解决, 如果其中一个操作比较复杂,要进行

这样还不能足够表现flatMap的好处. 先看一段传统写法的代码

func getValue(with stringValue: String) -> String {
        guard let number = Int(stringValue), number > 100 else {
            return "default"
        }   
        let str = String(number + 100)
        if str == "1000" {
            return str
        } else {
            return "default"
        }
    }

为了举例子写了一段没有具体需求的函数, 目的是为了让它无法简化多少.
我们看到在两种为nil的情况下我们写了两次return "default"
但是使用flatMap

 func getValue(with stringValue: String) -> String {
        return Int(stringValue).flatMap {
            return $0 > 100 ? nil : String($0 + 100)
        }.flatMap {
            $0 == "1000" ? "1000" : nil
        } ?? "default"
}

我们将两种可能为nil的情况传递到函数调用的末尾, 统一来返回default, 并且变化的过程看起来更简单清晰了
你可以试一下如何优化第一种代码.. 我是不知道它如何能做到一次处理返回default

总结

flatMap虽然传的是一个函数, 里面可以做额外的操作, 但是我们最好只做变形操作.不能引发副作用是函数式编程的原则..
另外Optional的这个函数虽然看起来挺酷, 但是使用不当也会增加阅读的复杂度,一个guard let就能解决的话还用个函数那就有些炫技的嫌疑了.
对于flatMap的nil下沉我觉得是与其它解包方式最大的区别. 大家也可以多试试.

flatMapmap区别

在函数式编程里所有的集合类型都有这两个方法,
除了集合类型, RxSwift里的Observable,和标准库里的Optional也有这两个方法, 它们的共性是对某一类型的元素进行了封装.
我们先将这个封装了之后的类型叫做容器, 被封装的值叫做元素
在集合类型里的元素很好理解, Optional容器里的元素就是someValue, Observable容器的元素就是观察的值

mapflatMap都是容器将元素进行变形得到一个新的容器, 不同的是
map的变形是 元素 -> 元素
flatMap的变形是元素->容器
套用到前面的几种容器类型就发现确实如此.... 主要区别在于transform函数的类型.

你可能感兴趣的:(Swift - 使用Optional的flatMap来让nil向下传递)