采用以下协议(protocol)和扩展:
protocol ProtocolA {
func myFunc()
}
extension ProtocolA {
func myFunc() {
print("Default ProtocolA implementation.")
}
}
这和将函数完全排除在协议(protocol)定义之外有什么区别(如果有的话),如下所示:
protocol ProtocolB { }
extension ProtocolB {
func myFunc() {
print("Default ProtocolB implementation.")
}
}
我发现了一个不同之处。如果我定义一个覆盖默认实现的结构,我只能将它转换为协议(protocol)并在我将函数保留在定义之外时调用协议(protocol)的实现:
struct A: ProtocolA {
func myFunc() {
print("Struct A's implementation.")
}
}
struct B: ProtocolB {
func myFunc() {
print("Struct B's implementation.")
}
}
A().myFunc() // "Struct A's implementation."
(A() as ProtocolA).myFunc() // "Struct A's implementation."
B().myFunc() // "Struct B's implementation."
(B() as ProtocolB).myFunc() // "Default protocol implementation."
换句话说,如果您像 ProtocolB
那样从协议(protocol)定义中 获取函数,那么您可以通过将对象强制转换为协议(protocol)来访问默认实现。另一方面,如果您将函数保留在协议(protocol)定义中,则无法强制转换为协议(protocol)以获得默认协议(protocol)行为。
将函数定义排除在协议(protocol)之外似乎可以在行为方面实现最大的灵 active 。
缺点是什么?如果将函数从协议(protocol)定义中移除,您会失去什么?您是否完全失去了任何功能?
最佳答案
将函数声明为协议(protocol)定义的一部分指示编译器在调用函数时使用动态分派(dispatch),因为编译器希望实现协议(protocol)的类型为该函数提供实现。这称为方法要求
。现在,如果类型未定义方法,则运行时会将方法调用解析为协议(protocol)扩展中声明的方法。
然而,在协议(protocol)扩展中声明函数仅告诉编译器他不需要使用动态调度,而是使用静态调度,这更快,但不不能很好地与多态性一起工作,因为即使符合协议(protocol)的类型也实现了该方法,协议(protocol)扩展实现也会被调用。
为了举例说明上述内容,让我们考虑以下代码:
protocol Shape {
func draw()
}
extension Shape {
func draw(){
print("This is a Shape")
}
}
struct Circle: Shape {
func draw() {
print("This is a Circle")
}
}
struct Square: Shape {
func draw() {
print("This is a Square")
}
}
let shapes: [Shape] = [Circle(), Square()]
for shape in shapes {
shape.draw()
}
上面的代码会有输出
This is a Circle
This is a Square
这是因为draw()
是一个方法要求
,意味着当draw()
被调用时,runtime会搜索draw ()
在元素的实际类型中的实现,在本例中是在 Circle
和 Square
中。
现在如果我们不将 draw
声明为方法要求,这意味着我们不会在协议(protocol)声明中提及它
protocol Shape {
}
那么编译器将不再使用动态调度,直接进入协议(protocol)扩展中定义的实现。因此代码将打印:
This is a Shape
This is a Shape
此外,如果我们将数组的一个元素向下转换为我们期望的类型,那么我们就会得到重载行为。这将打印 This is a Circle
if let circle = shapes[0] as? Circle {
circle.draw()
}
因为编译器现在能够分辨出 shapes
的第一个元素是一个 Circle
,并且因为 Circle
有一个 draw()
方法,它将调用该方法。
这是 Swift 处理抽象类的方法:它为您提供了一种方法来指定您希望从符合该协议(protocol)的类型中得到什么,同时允许这些方法的默认实现。
关于swift - 在协议(protocol)定义中包含功能与仅在扩展中定义它的原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34777926/