C++ VS Swift
虽然我很早就了解了Swift(2014年的WWDC),但是在粗略看了一下Swift的语法后,我认为这不过是许多语言语法的大杂烩,感觉和C++没有啥区别。但是实际上,在我使用Swift的这几个月中,我发现了许多问题值得注意的地方,比如
- 函数的返回值可以作为推断函数签名的依据。
- Swift中的函数静态Dispatch VS 动态函数Dispatch
而第二点,也是本文要阐述的重点。
在展开本文的内容前,如果你曾经有C++的开发背景,不妨回忆下C++中RTTI机制,这也是多态发生的先决条件。简单来说,就是C++的多态函数是基于运行时的,我们可以看看下面这个例子:
class A
{
void print(){cout << "I'm A";}
}
class B: public A
{
void print(){cout << "I'm B";}
}
A *b = new B();
b->print();
相信大家一眼就能知道这个答案,会输出I'm B
。那么,Swift中也存在class
,那么对于Swift中的函数调用是否和C++一致呢?
Swift Class
首先我们先来验证下最基本的class
中的行为。我们采用和C++中相同例子,定义如下:
class A
{
func printInfo()
{
print("I'm A")
}
}
class B:A
{
override func printInfo()
{
print("I'm B")
}
}
根据Swift的Type Inference
我们分别验证了如下几种调用方式:
let b1:B = B()
b1.printInfo() // I'm B
let b2:A = B()
b2.printInfo() // I'm B
let a1:A = A()
a1.printInfo() // I'm A
let a2 = A()
a2.printInfo() // I'm A
let b3 = B()
b3.printInfo() // I'm B
如果你自己的思考结果和这个一模一样,至少你理解了运行期和编译期的概念,恭喜你,你的C++是过关了。以class B
举例,无论是b1, b2, b3中的哪一个,尽管其中有部分声明的类型是A,但是在实际运行时还是会走类似virtual function那套确认实际类型为B。但是,事情在Swift中真是这么简单吗?让我们接着往下看。
Protocol Extension
去年,Swift 2.0发布,随之而来,一个概念悄然兴起:面向协议的编程。而这种编程范式不可或缺的必要条件就是Protocol Extension。在Swift < 2.0
时代,Protocol
的作用更类似于一种表征特征的约束。而有了Protocol Extension
以后,Protocol
更类似于一种插件装配的概念(写过Ruby的人相信会有体会),可以在无须编写代码的情况下,更自定义的元素添加行为能力。
哎?你上面说了这么一大段废话,和我们的文章主题有啥关系?
好,首先我们先看如下定义:
protocol Testable
{
func dynamicInfo()
}
extension Testable
{
func dynamicInfo()
{
print("I'm Testable")
}
}
class A:Testable
{
func dynamicInfo()
{
print("I'm A")
}
}
class B:Testable
{
func dynamicInfo() {
print("I'm B")
}
}
然后,我们进行如下调用:
let a1 = A()
a1.dynamicInfo() // I'm A
let b1 = B()
b1.dynamicInfo() // I'm B
let a2:Testable = A()
a2.dynamicInfo() // I'm A
let b2:Testable = B()
b2.dynamicInfo() // I'm B
到这里,事情还是还是按照C++那套逻辑在走,如果你把class B
中的dynamicInfo
删除,那么对应B类型的dynamicInfo
函数调用就会输出I’m Testable。
好,现在问题来了,如果我们将Testable Protocol Extension
添加一下东西,同时保持protocol Testable
不变,如下所示:
protocol Testable
{
func dynamicInfo()
}
extension Testable
{
func dynamicInfo()
{
print("I'm Testable")
}
// ### 新添加的 ###
func staticInfo()
{
print("I'm Testable Static")
}
}
如果这个时候,我们进行如下代码的测试:
class A:Testable
{
func dynamicInfo()
{
print("I'm A")
}
func staticInfo()
{
print("I'm A Static")
}
}
let a1 = A()
a1.staticInfo() // I'm A Static
let a2:A = A()
a2.staticInfo() // I'm A Static
let a3:Testable = A()
a3.staticInfo() // I'm Testable Static
看到没?最后一行的输出是不是出乎了大家的意料,竟然输出了I’m Testable Static。
这是咋回事?回顾下之前我们改变的地方,发现我们在Protocol Extension中添加了一个func staticInfo()
,但是却没在对应的Testable Protocol
进行声明。但是这还不够,我们必须将调用staticInfo
的地方的类型显式的声明成let a3:Testable。
- 方法在Extension中提供了实现,但是在对应的protocol中没有声明。
- 调用方法的时候必须显示的声明成protocol的类型。
如果不好理解,我画了张图帮助大家加深印象:
还有一点需要注意的是,Swift中的静态Dispatch不以类的层级和override而转移,也就是说,如下这种定义:
class B:A
{
override func staticInfo()
{
print("I'm B Static")
}
}
当我们使用
let a4:Testable = B()
a4.staticInfo()
一样会输出I'm Testable Static