协议是 Swift 中头等重要的内容,在实际开发中,我们会大量地使用面向协议编程。
方法的默认实现
一个协议中可能会定义许多的属性和方法,有些属性和方法却不是遵循该协议的对象必须实现的,又或者,我们不希望在每个遵循该协议的对象中写入过多重复的代码。
我们可以给协议的属性和方法添加默认的实现来解决上面的问题,我们先来看看方法的默认实现。
第一种方式是使用 @objc
来实现:
1 | protocol Human { |
Human 协议定义了两个方法,其中 eat
为必须实现的,而 read
并不是必须的,使用 @objc optional
表明该方法为可选。然后我们让 Man 这个类遵循 Human 协议,只需要实现 eat
方法即可。
第二种实现方式是使用 extension
给协议添加一个默认实现方法:
1 | protocol Human { |
我们在协议扩展中默认实现了 read
方法,因此在结构体 Man 中就无需实现该方法了。
注意这里我将 Man 的类型从 class
改为了 struct
,这样是没有问题的。但是如果我们在第一种实现方式中,将 Man 的类型从 class 改为 struct,编译器就会报错。
因为使用 @objc
标记的协议,只有遵循 NSObject 的 class 类才能遵循该协议,这也是它的局限性所在。比如 struct
、enum
都不能遵循该协议,关联类型更是无法在该协议中使用。
使用 extension
为协议添加默认实现虽然需要多写点代码,但却摆脱了上面的限制,笔者个人习惯使用第二种实现方式,这也是笔者推荐的方式。
属性的默认实现
我们给 Human 添加一个属性 age
和 height
,然后添加一个新的结构体 Woman ,代码变成如下:
1 | protocol Human { |
毫不意外,编译器报错了,因为 Man 和 Woman 没有实现协议中的 age 和 height 属性,我们当然可以在 Man 和 Woman 中实现这两个属性,但是如果还有其它遵循 Human 协议的结构体,我们不想在每个结构体中都把这两个属性实现一遍,而是希望能有个默认值。那么该如何实现呢?很遗憾 extension 无法实现我们的需求,我们需要另辟蹊径。
在软件设计中,许多问题都可以通过添加中间层来实现。这里我们可以借鉴这个思路,先定义一个如下结构体将各个属性包装起来:
1 | struct HumanProperties { |
然后我们在 Human 协议中添加一个属性:
1 | protocol Human { |
然后我们在扩展中添加属性:
1 | extension Human { |
然后在遵循 Human 协议的结构体中实现 hp 属性:
1 | struct Man: Human { |
不管 Human 有多少属性要实现,我们都可以使用 HumanProperties 包装起来,并且为之提供了默认值。
方法参数的默认值
我们为 eat 方法添加如下参数:
1 | func eat(breakfast: String, lunch: String, dinner: String) |
假设我们现在正在健康饮食阶段,想给 breakfast 和 dinner 添加两个默认值来约束 Man 的行为。
遗憾的是,协议方法并不支持带默认值的参数。
我们可以故技重施,将上面的参数包装起来:
1 | struct Recipe { |
然后将方法修改为:
1 | func eat(_ food: Recipe) |
接着实现该方法:
1 | struct Man: Human { |
调用:
1 | var p = Man() |