Array vs. Optional
所以,作为一个提醒,我们在前面的文章中了解到,Array
map( transform: T -> U ) -> Array
flatMap( transform: T -> Array ) -> Array
这意味着,给定一个变换:T-> U的用户可将T的数组转换为U的数组,只需使用(变换:T-> U)在你的Array
好了,这并不奇怪,map()和flatmap的返回值(Optional
map( transform: T -> U ) -> Optional<U>
flatMap( transform: T -> Optional<U> ) -> Optional<U>
map() on Optionals
那么,map方式是如何返回Optional
嗯,这很简单 :与Array
你想想看,这非常像Array
回到我们的例子
那么,怎样才能应用这一切在代码中呢?
在上个版本的代码中,我们有一个itemDesc[“icon”]做了隐式转换(String?),并且我们想要把它最终转化成为UIImage;但是UIImage(named:)使用String作为参数,而不是String?(Optional)类型,所以我们需要使用一个方法的内部值String,并且仅仅当可选变量不为nil的情况。
一个解决办法是使用Optional绑定来做到这一点:
1 |
|
但是,对于这样一个简单的操作,我们写了太多行。
在前面的例子中,我们使用了(非常不优雅)替代解决方案,采用了nil,合并运算符??:
1 |
|
上面的代码能够工作,仅因为如果iconName是nil,我们将欺骗的UIImage的初始化和使用UIImage(name:” “),这确实会返回一个nil的图像。但是,这样并不能让人感觉干净整洁,我们有点滥用初始化,使其在这里工作。
让我们使用map
那么,为什么不使用map?的确,我们想要解包我们的Optional
我们试一试:
1 |
|
等等,上面的代码不能编译。你能猜出是为什么吗?
发生了什么?
问题出在UIImage(name:…)方法返回了一个可选类型:如果name没有值,那么它将无法创建UIImage,这样就使得这个初始化是失败的,在这种情况下将返回nil。
因此,这里的问题是,我们给闭包映射一个String,并返回……一个UIImage?类型 –初始化失败并且返回nil的类型。并且如果你再次检查map内部,他将要形成T(imageName) -> U(UIImage(name: ImageName))的闭包,并且返回U?。所以在这种情况下,U相当于UIImage?并且如果整个map表达式返回U?那么最终将返回的将是UIImage??,是的,两个Optional,oh boy!
flatMap()来救援
flatMap与map相似,但是它使用T->U?转换(而不是T->U),并且它是”flattens”的它的结果仅有一级的optional。这正是我们期望的!
1 |
|
因此,这里就是flatMap实际所做的:
1、如果iconName为nil,flatMap将直接返回nil(但是类型为UIImage?)
2、如果iconName不为nil,flatMap适用于转换内部值,因此试图使用String创建UIImage,并且返回结果–如果已经是UIImage?因此其可以是nil,如果UIImage初始化失败的话。
总之,item.icon只会有一个非空值,如果 itemDesc[“icon”]as? String 为非空并且UIImage(named:imageName)初始化成功。
这样感觉好多了,比欺骗初始化的??更加符合习惯。
使用 init闭包
说远一点,上面也可以使用一个更紧凑的编码方式,如Xcode中7现在通过类型的.init属性公开这些类的构造函数。
这意味着,UIImage.init实际上已经是一个函数,它接受一个字符串,返回一个UIImage?因此,我们可以直接使用它作为参数传递给我们的flatMap,没必要把它放在闭包里!
1 |
|
好吧,我觉得这是难以阅读和个人更喜欢使用一个明确的闭包在这里,为了使代码更清晰,更加明确。但是,这是个人喜好的问题而已,这是一件好事,你知道这是可能的!
我们最终的swift代码
1 |
|
回头看看我们的ObjC代码
花一点时间来与ObjC代码比较,我们的WIFT代码。我们从那里适应了相当多的东西!
如果你仔细观察ObjC与swift的代码,你就会意识到,swift的代码行数不会明显地缩短,但它的方式更安全。
特别是,关于swift,我们学会了,guard?,try?和as?它迫使我们必须检查一切不确定的类型,那些ObjC代码不会操心的和会导致崩溃的潜在问题。因此,也许它们的代码一样大小,但ObjC代码的更加危险!
结论
有了这个系列文章,我希望你明白,你不应该尝试把ObjC代码直接转化为swift。相反,尝试重新思考你的代码,重构它。通常更好的方式是从一个clean的状态开始重写你的代码时考虑到swift的特性,而不是试图直接翻译ObjC代码。
我不是说这很容易。改变你的思维方式,当你已经习惯了ObjC,并使用其模式和方法来编写代码还可能需要一些时间。但swift肯定更好。