转载

Map all the things in Swift 映射所有东西

在上一篇文章中我们为数组引入了map()和flatMap(),详细讲解了他们的用法和优点,其实map和flatMap也适用于Optionals类型 和 其他很多的类型,今天我们来探索下他们的用法。

对比下Array 和 Optional

回忆一下,我们之前学习的在Array上使用map和flatMap的用法是这样的:

// Method on Array<T>     map( transform: T ->          U  ) -> Array<U> flatMap( transform: T ->    Array<U> ) -> Array<U>   

这意味着如果提供一个转换方式:T->U的话,你可以将一个包含T的Array转换成一个包含U的Array。也就是说让 Array<T> 通过简单的调用map( transform: T->U ),就会返回一个 Array<U>

类似的,map和flatMap在 Optional<T> 上的用法是这样的:

// Method on Optional<T>     map( transform: T ->          U  ) -> Optional<U> flatMap( transform: T -> Optional<U> ) -> Optional<U>   

map() on Optionals

ok,我们来看看map()在 Optional<T> 类型上做了什么。同理于 Array<T> ,首先是拿到了 Optional<T>(Array<T>) 里的所有内容 ( Array<T> 的内容为数组里面所有T, Optional<T> 里所有内容为该Optional的真实值和nil) ,然后根据我们所提供的转换方式(transform: T->U)进行转换后,再将结果包装成新的 Optional<U>(Array<U>) 。他们所做的事情其实是一样的。

看回我们的例子

我们来看看怎样在我们之前的代码里用上我们所说的方法。

记得在上一篇文章我们的示例代码中,我们的 itemDesc["icon"] 会返回一个 String? ,而我们的目的是将其作为图片名字转换成一个 UIImage ,只不过, UIImage(name:) 需要接收一个 String 类型的参数而不是 String? ,所以我们保证在这个Optional String值非nil的情况下,并拿到Optional里真实的String去调用这个UIImage的构造函数。

当然在以前,我们可以使用Optional Binding来做:

let icon: UIImage?   if let iconName = itemDesc["icon"] as? String {     icon = UIImage(named: iconName) } else {   icon = nil } 

为了更加简便,节省代码行数,我们还可以用 nil联结操作符 ??

let iconName = itemDesc["icon"] as? String   let icon = UIImage(named: iconName ?? "")   

当iconName为nil时,将""赋值给iconName作为UIImage构造函数的参数: UIImage(named: "") ,返回一个nil的image(UIImage?),保证了程序不crash,但是这样似乎有点扭曲了构造函数 UIImage(named:)

我们用map试试

事实上我们需要的是当Optional 非nil的时候将其解包,并且将他的真实值作为参数传递给 UIImage(named: ) 使其返回一个UIImage,所以这是这非常贴切的用法。 上代码:

let iconName = itemDesc["icon"] as? String   item.icon = iconName.map { imageName in UIImage(named: imageName) }   

想法是对的,可是上面的代码必须是编译不过的!问题出在哪里呢?先解释下上面这段代码。我们让iconName这个Optional值调用map()方法,映射其非nil的值(真实值){换句话说:当iconName非nil时执行map闭包里的转换规则}成为参数imageName传入闭包,构造出一个UIImage对象。问题就出在如果imageName不是一个有效图片名字,或者因为某种原因取不到的话,UIImage就为nil,所以 UIImage(named: ) 返回值本身就是一个 UIImage? ,再看看map()的定义:使转换T->U并返回 U? 。那么再看看我们的例子,UIImage?就相当于 U ,那么整个map完了后会返回的 U? 是什么?------ UIImage?? 。哈哈这就是所谓的Double-Optional!

救星faltMap

flatMap做了什么转换? T->U? ,因其使结果扁平化(flattens)而得名。。。。介绍这些就不再说了,回顾可以看回前一篇文章。在这里flatMap可以为我们去掉一层optional:

let iconName = itemDesc["icon"] as? String   item.icon = iconName.flatMap { imageName in UIImage(named: imageName) }   

因此,在这个应用中,flatMap做了这些事情:

-如果iconName为nil的话,直接返回nil而不是作为一个UIImage?来返回

-如果iconName有值,flatMap将尝试使用iconName来创建UIImage,这时只有UIImage的构造函数失败了,才会返回nil,因此flatMap返回值为UIImage?

总而言之,只有在 itemDesc["icon"] as? String 结果非nil,并且 UIImage(named: ) 成功的情况下, item.icon 才有一个非空值。

这样做比使用 ?? 来欺骗构造方法更中规中矩。

魔法:使用init简化闭包

在Xcode7 后构造器可以通过 .init 这个property暴露出来,因此我们可以进一步地让代码更简化更紧凑。就是说 UIImage.init 是一个参数为String返回值为UIImage?的方法,我们可以直接地让他成为flatMap的参数,而简化掉闭包:

let iconName = itemDesc["icon"] as? String   item.icon = iconName.flatMap(UIImage.init)   

。。。。这个用法我还没有理解得非常通,不过好像仅仅在IOS8以上生效,最后谢谢捧场!

正文到此结束
Loading...