swift思维 第三部分:struct

这次让我们使用struct来简化我们的代码

在本系列的上一篇文章中,我们学习了如何使用array的map和flatMap方法,从而避免出现有状态的中间变量,并且使用一些函数式编程。
翻译自

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

class ListItem {
var icon: UIImage?
var title: String = ""
var url: NSURL!

static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
guard let nonNilJsonData = jsonData,
let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
let jsonItems = json as? Array<NSDictionary>
else {
return []
}

return jsonItems.flatMap { (itemDesc: NSDictionary) -> ListItem? in
guard let title = itemDesc["title"] as? String,
let urlString = itemDesc["url"] as? String,
let url = NSURL(string: urlString)
else { return nil }
let li = ListItem()
if let icon = itemDesc["icon"] as? String {
li.icon = UIImage(named: icon)
}
li.title = title
li.url = url
return li
}
}
}

今天,我们要做一个非常简单的变化,它会使我们的代码更轻量,看起来更像swifteer。

struct vs class

我们刚刚使用swift的时候通常使用class,这不难理解,因为在oc中class无处不在。使用class并没有什么错误,你当然可以在swift中继续使用它们。但是在swift中,struct拥有更强大的功能相对于c语言来说。它们不再局
限于一些拥有值的字段。

相对的,swift的struct拥有class相同的能力,除了继承,它还拥有值类型(所以当你使用其他的变量时,都会复制它们,就像Int)而class是引用类型,通过引用用而不是复制,在objective-c中(它们遍布了每个地方)下一个是转贴,请参见watch this excellent talk from Andy Matuschak on that subject关于两者的解释。

转换我们的class为struct

在这种情况下,struct似乎更适合,因为它承载值,并且不会被某些意外所改变(复制胜于引用)。假如我们要使用它们作为数据源例如一个菜单,并且它们并非旨在一定要创建中间量,所以这种情况是有意义的。

此外,struct的优势在于他们有一个隐含的构造函数在默认的情况下,如果你不定义:我们可以使用它的默认构造函数列表项ListItem(icon:…,titile:…,url:…)。

最后但是并非最不重要的,因为我们现在还不能创建一个伪造的列表项,因为我们消除了上面的数据损坏的问题,我们可以消除tittle的默认值,但更重要的是,我们可以save that last pony ??通过将NSURL!转换为NSURL。

所以接下来的代码像这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

struct ListItem {
var icon: UIImage?
var title: String
var url: NSURL

static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
guard let nonNilJsonData = jsonData,
let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
let jsonItems = json as? Array<NSDictionary> else { return [] }

return jsonItems.flatMap { (itemDesc: NSDictionary) -> ListItem? in
guard let title = itemDesc["title"] as? String,
let urlString = itemDesc["url"] as? String,
let url = NSURL(string: urlString)
else { return nil }
let iconName = itemDesc["icon"] as? String
let icon = UIImage(named: iconName ?? "")
return ListItem(icon: icon, title: title, url: url)
}
}
}

现在,我们使用一步来创建ListItem,因为struct为一个默认的init()内包含所有的参数,如果你不提供任何自己的初始化参数。我们可用class做相同的事,但是一旦有class我们就不得不初始化自己。

合并运算符

在上面的例子中,我还使用了新的技巧,使用?操作符得到一个默认情况下iconName为nil的情况。

这里的??操作符类似于Objective-c的opt?:val号运算符,这意味着,如果opt是类型T的?VAL必须是T型的。

所以在这里iconName??””让我们用一个nil的情况下的iconName的映射,我们知道会导致nil的UIImage(icon=nil)的这种情况。

⚠️注意⚠️:这不是处理一个空的iconName或者有一个nil的UIImage最好的和干净的方式。事实上,它甚至显得有点丑陋,以及使用一个假的“”符号创建一个空的图像。但是,这是一个机会,向您展示??的用法……哎,我们也保留了一些不错的东西,作为本作的下一部分。

有问题请回复