Protocol Extensions

资源推荐:

此文为 The Best of What’s New in Swift 有关Protocol Extensions的翻译。

Protocol Extensions

在Swift 2.0 之前, Objective-C和Swift,在protocols中只能包含方法声明。 protocols只包含接口定义即一堆等待遵守类型去实现的方法。 在Swift 2中使用 protocol extensions, protocols可以既包含声明,也可以包含实现。

经常,会有很多功能性方法适用于一类对象。 如, 所有的集合类型都可以有将一个集合内对象经过映射(map),而创建一个映射后的集合对象。 在Swift 2之前, 有两种方式可以达到这种能力。

* 在protocol中声明方法并要求对应遵循接口的对象实现自己的方法
* 全局函数

Cocoa基本上采用的是第一种解决方案。 虽然这些方法可能太适合成为任何protocol的一部分, 但是Cocoa的集合对象都有一个enumerateObjectsUsingBlock:方法,每个集合类型有各自的实现。

Swift 2之前,采用的是第二种解决方案。 想 map 这种全局函数,操作于 CollectionType . 这种方式很好的共享了函数实现, 但是语法奇怪并且不能针对特定类型重写实现方法。

有了 protocol extensions , 我们可以有了更牛X的第三种方式。 map 可以实现在 CollectionType 的一个扩展(extension)中。 所有遵循 CollectionType 的类型,就自动获得了一个 map 实现。

一下为一个简单的 map 函数实现:

1
2
3
4
5
6
7
8
9
10
11
12
extension CollectionType {
func myMap<U>(f: Self.Generator.Element -> U) -> [U] {
var result: [U] = []
for elt in self {
result.append(f(elt))
}
return result
}
}

[1, 2, 3, 4].myMap({ $0 * 2 })
// This produces [2, 4, 6, 8]

之前只可以作为 Array 的扩展,然后只可以在 Array 中使用。 有了一个接口的扩展, 上面的实现也可以在 Set 和 ArraySlice 和其他遵循 CollectionType 接口的类型中获得。

另一个很有意思的Swift protocol extensions特性是可以对类型做出限制。 举个栗子, 如果你想实现一个 max 属性。 但是 max 并不适用于所有集合类型, 只有那些有序集合才使用这个属性。 没问题! 只要给扩展加一个限制,要求其实现类型必须遵守 Comparable 协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extension CollectionType where Self.Generator.Element: Comparable {
var max: Self.Generator.Element {
var best = self[self.startIndex]
for elt in self {
if elt > best {
best = elt
}
}
return best
}
}

Set([5, 4, 3, 2, 1]).max
// This produces 5

[NSObject(), NSObject()].max
// This produces an error, as NSObject is not Comparable

protocol extensions 有一个会令人迷惑的地方, 虽然很小但是很重要,决定了protocol extension中方法是否需要动态分发(dynamic dispatch)。

一个在protocol extension中声明的方法也许也会在protocol中声明, 或者只存在于extension中。 只存在extension中的方法,不会动态分发(dynamic dispatch)并且也不能被复写。只声明在protocol中的方法是动态分发(dynamic dispatch)的并且可以被复写。talk is cheap, show me the code:

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
protocol P {
func a()
}

extension P {
func a() {
print("default implementation of A")
}

func b() {
print("default implementation of B")
}
}

struct S: P {
func a() {
print("specialized implementation of A")
}

func b() {
print("specialized implementation of B")
}
}

let p: P = S()
p.a()
p.b()

上面的代码会打印出:

1
specialized implementation of A和default implementation of B.

使用protocol extensions还可以让Swift的protocol方法成为“可选”方法,在Swift 2中,我们可以这样实现一个delegate中的可选方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

protocol MyClassDelegate {
func shouldDoThingOne() -> Bool
func shouldDoThingTwo() -> Bool
}

extension MyClassDelegate {
func shouldDoThingOne() -> Bool {
// Thing one is harmless and should almost always be done
return true
}

func shouldDoThingTwo() -> Bool {
// Thing two is a menace and should not be done without careful consideration
return false
}
}