承接上一篇博文,上一篇的篇幅有点太长了,我觉得有一些相关的技术点需要说明,所以重新写几篇博文。这个系列的文章将要说明以下几个问题:
1.giveData和getData在各自协议中的位置
2.使用struct代替class的好处
3.“幽灵架构”为什么不会产生循环引用
4.协议的应用场景与局限性
5.运用面向协议编程思想改造控制器
让我们来简单回顾下这个架构,如果不明白的可以参考上一篇博文:
核心只有两个协议:
//视图使用的协议
protocol ViewType{
func getData<M:ModelType>(model:M)
}
//数据使用的协议
protocol ModelType{
}
//定义默认方法giveData
extension ModelType{
func giveData<V:ViewType>(view:V){
view.getData(self)
}
}
首先来回答第一个问题,为什么getData被定义在协议ViewType的定义中,只有函数声明没有实现,而giveData被定义在ModelType的扩展中,并且附带了方法的具体实现。请看下面的例子:
protocol SharedString{
}
extension SharedString{
func methodForOverride(){
print("(。•ˇ‸ˇ•。)")
}
func methodWithoutOverride(){
print("------")
methodForOverride()
print("------")
}
}
两个方法都被定义在了协议的扩展中,现在让String遵守该协议,String将免费获得这两个方法的实现,并且在String的扩展中重写methodForOverride()的实现,输出String本身的值:
extension String:SharedString{
func methodForOverride(){
print(self)
}
}
现在使用字面量生成一个String类型,然后调用方法methodForOverride:
"hello".methodForOverride()
输出的结果是“Hello”,这符合我们的预期,这里使用字面量生成了一个实例,依靠了类型推断的默认值,“Hello”是Sting类型的,也就是SharedString的具体遵守者,所以调用了被重写的methodForOverride版本。Swift中的方法重载不仅可以根据参数进行重载,还可以根据返回值类型进行重载,只需要修改返回值的声明即可。这里我们修改“hello”的上下文:
//用一个字面量来跟大家打个招呼
"hello".methodForOverride()
//hello是SharedString类型的
let hello:SharedString = "hello"
hello.methodForOverride()
hello是SharedString类型的,虽然hello和“hello”的字面量是相同的,打印结果如下:
有趣的是在协议的扩展中声明的方法定义会被保留,更有趣的是另一个方法methodWithoutOverride(),这个方法在方法体中调用了另一个方法methodForOverride,现在对“hello”和hello调用方法methodWithoutOverride:
"hello".methodWithoutOverride()
hello.methodWithoutOverride()
结果如下:
结果有点出乎意料,虽然我们在协议遵守者的定义中重写了方法methodForOverride,定义在协议扩展中的另一个方法只会使用methodForOverride的默认版本的实现,这是协议扩展的静态特性。现在把methodForOverride的声明从协议的扩展中挪到协议的声明中:
protocol SharedString{
func methodForOverride()
}
extension SharedString{
func methodForOverride(){
print("(。•ˇ‸ˇ•。)")
}
func methodWithoutOverride(){
print("------")
methodForOverride()
print("------")
}
}
现在无论你使用“hello”还是hello调用methodForOverride()和methodWithoutOverride()都只会输出重写后的结果。
结论:把需要被重写的方法声明在协议的声明中是一种刻意的做法,我们希望该方法被重写,回到架构上,getData负责不同的View处理获得的Model,View的类型非常多,所以getData的格式无法统一到一个默认的方法实现中,我们需要getData被具体的View所重写,为了强制这种意图,getData甚至没有一个默认的实现方法体。根据Swift的协议规则,协议遵守者需要实现协议中的所有方法,所以getData一定会有具体声明。而前文也总结过,对于ModelType中的giveData这个方法的功能非常统一,即向View传递Model,所以统一到同一个方法实现中,并且我们不希望这个方法被重写,因为重写会破坏getData中的逻辑,所以把方法声明写在了协议的扩展中。
最后需要说明的是“幽灵架构”这个名字只是想表达代码隐形的愉悦,其实名字本身并不重要。