什么是swift协议?
Protocol
Swift标准库中有50多个复杂不一的协议,几乎所有的实际类型都是妈祖若干协议的。protocol是Swift语言的底座,语言的其他部分正是在这个底座上组织和建立起来的。这和我们熟知的面向对象的构建方式很不一样。
一个最简单但是有实际用处的Swift协议定义如下:
protocol Greetable {
var name: String{
get}
func greet()
}
这几行代码定义了一个名为Greetable的协议,其中有一个name属性的定义,以及一个greet方法的定义。
所谓协议,就是一组属性和/或方法的定义,而如果某个具体类型想要遵守一个协议,那它需要实现这个协议所定义的所有这些内容。协议实际上做的事情不过是“关于实现的约定”。
面向对象
在深入Swift协议的概念之前,我想先重新让大家回国一下面向对象。相信我们不论在教科书或者是博客等各种地方对这个名词都十分熟悉了。那么又一个很有意思,但是其实并不是每个程序员都想过的问题,面向对象的核心思想究竟是什么?
class Animal {
var leg:Int {
return 2}
func eat() {
print("eat food")
}
func run() {
print("run with \(leg)legs")
}
}
class Tiger:Animal {
override var leg: Int {
return 4}
override func eat() {
print("eat meat")
}
}
let tiger = Tiger()
tiger.eat()//eat meat
tiger.run()//run with 4 legs
父类Animal 定义了动物的leg,以及动物的eat和run方法,并未她们提供了实现。子类的Tiger根据自身情况充血了leg和eat,而对于run,父类的实现已经满足要求,因此不必重写。
我们看到Tiger和Animal共享了一部分代码,这部分代码还被封装到了父类中,而除了Tiger的其他的字类也能够使用Animal的这些代码。这其实就是OOP的核心思想-使用封装和集成,将一些列相关的内容放到一起。我们的前辈们为了能够对真实的世界的对象进行建模,发展出了面向对象编程的概念,但是这套理念有一些缺陷。虽然我们努力用这套抽象和集成的方法进行建模,但是实际的食物往往是一系列特质的组合,而不单单是以一脉相承并逐渐扩展的方式构建的。所以最近大家越来越发现面向对象很多时候其实不能很好的对食物进行抽象,我们可能需要寻找另一个更好的方式。
面向对象编程的困境
横切关注点
我们再来看一个例子。这次让我们远离动物界,回到Cocoa,假设我们又一个VIewController,它集成自UIViewController,我们向其中添加一个myMethod:
class ViewController:UIViewControlller{
func myMethod() {
}
}
如果这个时候我们又有一个继承自UITableviewController的AntherViewController,我们也想向其中添加同样的myMethod:
class AnotherViewController:UITableViewController{
func myMethod(){
}
}
这是我们迎来了OOP的第一大困境,那就是我们很难再不同集成关系的类里共用代码。这里的问题用“行话”来说叫做“横切关注点(cross-cutting concerns)”。我们的关注点myMethod位于两条继承链的横切面上。面向对象是一种不错的抽象方式,但是肯定不是最好的方式。它无法描述两个不同食物具有某个相同特性这一点。在这里,特性的组合要比集成更贴切事物的本质。
想要解决这个问题,我们有几个方案:
- Copy&Paste
这是一个比较糟糕的解决方案,但是演现场还是有不少朋友选择了这个方案,特别是在工期很紧,无暇优化的情况下。这诚然可以理解,但是这也是坏代码的开头。我们尽量避免这种做法。 - 引入BaseViewControlller
在一个集成自UIViewController的BaseViewControlller上添加需要共享的代码,或者干脆在UIViewController上添加extension。看起来这是一个稍微靠谱的做法,但是如果不断这么做,会让所谓的Base很快变成垃圾堆。指责不明确,任何东西都扔进Base,你完全不知道那些类走了Base,而这个超级类对代码的影响也会不可估量。 - 依赖注入
通过外界传入一个带有myMethod的对象,用新的类型来提供这个功能。这是一个稍好的方式,但是引入额外的依赖关系,坑能也是我门不太愿意看到的。 - 多继承
当然,Swift是不支持多继承的,不过如果有多集成的话,我们确实可以从多个父类进行集成,并将myMethod添加到合适的地方。有一些语言选择了支持多集成,但是它会带来OOP中另一个著名的问题:菱形缺陷
菱形缺陷
上面的例子中,如果我们有多继承,纳闷ViewController和AnotherViewController的关系可能会是这样的:
在上面这种拓扑结构中,我们只需要在ViewContorller中实现myMethod,在AnotherVIewController中也就可以继承并使用它了。看起来很完美,我们避免了重复。但是多集成有一个无法回避的问题,就是两个父类都实现了同样的方法时,自乐该怎么办,我们很难确定因该继承哪一个父类的方法。因为多集成的拓扑结构是一个菱形,所以这个问题又被叫做菱形缺陷。像是C++这样的语言选择粗暴的将菱形缺陷的问题交给程序员处理,这无疑非常复杂,并且增加了人为错误的可能性。二绝大多数现代语言对多集成这个特性选择避而远之。
动态派发安全性
OC恰如其名,是一门典型的OOP语言,同时它继承了SmallTalk的消息发送机制。这套机制十分灵活,是OC的基础思想,但是有时候相当危险。
ViewController *v1 = ...
[v1 myMethod];
AnotherViewController *v2 = ...
[v2 myMethod];
NSArray *array = @[v1,v2];
for (id obj in array){
[obj myMethod];
}<