在Swift 2发布时,苹果将Swift定义为一门面向协议编程的语言,协议(Protocol)在Swift中被赋予了更加强大、灵活的功能。相比于Objective-C的协议,Swift的协议不仅可以被用做代理,也可以用作对接口的抽象,代码的复用。这里主要介绍下协议的扩展与协议的动态、静态派发。
面向协议编程
在面向对象编程中,所有的功能都被封装在一个个对象中,而在面向协议编程中,功能是被定义在一个个协议中。比如吃东西,我首先要定义一个对象、比如人,然后提供一个吃东西的方法,如果我再创建一条狗,也有吃东西的动作,这个时候要么写分别写两套吃东西的代码,要么对人、狗进一步抽象,抽出更基础的动物基类,把吃东西的方法写在动物基类里,让人、狗继承自动物。
而面向协议编程则不同,我们只需要定义一个吃东西的协议,所有需要吃东西动作的类都遵循这个协议即可,不需要为了这一个动作而继承一个基类。这使得接口的定义更加灵活,轻便。同时通过协议扩展,我们可以为定义的接口提供默认现实,达到代码复用的目的。
打一个比喻,面向对象编程的继承像套娃娃,一层继承就是套一个娃娃,面向协议编程就像搭积木,一个个协议就是一个个积木,一个协议就是一块积木,通过把多个积木组合起来实现不同的功能。
协议的扩展和协议的动态、静态派发
在Swift 2中,协议加入了强大的扩展功能,通过扩展,一是可以给协议定义中的方法提供默认的实现,二是可以在扩展中添加新的方法。这使得Swift中的协议能通过协议扩展实现代码的复用。
通过协议扩展进行代码共享与继承相比,有以下几个优势:
- 不需要强制使用某个父类
- 可以让已经存在的类型遵循协议
- 协议既可以用于类,也可以用于结构体和枚举
- 通过协议,不需要处理super方法的调用问题
使用协议扩展时需要注意的一点是,在协议定义声明了的方法是动态派发的,而只是在协议扩展中添加,并没有在定义中声明的方法,是静态派发的。
动态派发的作用就是,在执行时,会先去寻找具体类型中对这个方法的现实,如果有会调用这个类型的实现,如果没有,再调用协议扩展中的实现。而静态派发,就是调用者声明的类型是什么,就去类型中找这个方法的实现,如果类型声明为协议,即使这个类型中有这个方法的现实,也只会调用协议扩展中的实现。
比如我们定义一个Animal
的协议,定义了一个eat
的方法,在扩展中实现了这个方法,并扩展了一个run
方法,但没有在定义中声明run
方法:
1 |
|
接下来我们定义一个Person
:
1 | struct Person: Animal { |
然后用Person
创建一个Animal
类型的实例,分别调用eat
, run
方法:
1 |
|
会发现,执行eat
方法时,调用的是Person
的实现,执行run
方法时,调用的是Animal
扩展中的实现。如果把animal
的类型指定去掉,通过类型推断,它就是Person
类的实例,这时执行run
方法,就会调用Person
的实现。这就是协议的动态派发和静态派发。
需要注意的一点是,如果你在扩展中为协议的某些方法提供了默认实现,最好要在文档中说明遵循这个协议,需要实现哪些方法,不然一旦协议的方法变多,变复杂,没有文档说明,很难搞清楚到底要实现哪些方法。
协议的关联类型和Self
带有关联类型的协议和普通的协议是不同的,普通的协议可以作为一个单独的类型,像上面的例子中Animal
就是普通协议,let animal: Animal
中animal
就能指定它是Animal
类型,但如果带有关联类型,就不能被当做独立的类型来使用。和关联类型一样,带有Self
的协议也是不能被当做独立的类型来使用。