采取以下协议和扩展:
protocol ProtocolA {
func myFunc()
}
extension ProtocolA {
func myFunc() {
print("Default ProtocolA implementation.")
}
}
Run Code Online (Sandbox Code Playgroud)
它之间有什么区别,如果有的话,将函数完全从协议定义中删除,如下所示:
protocol ProtocolB { }
extension ProtocolB {
func myFunc() {
print("Default ProtocolB implementation.")
}
}
Run Code Online (Sandbox Code Playgroud)
我发现了一个区别.如果我定义一个覆盖默认实现的结构,我只能将它转换为协议并调用协议的实现,如果我将该函数从定义中删除:
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."
Run Code Online (Sandbox Code Playgroud)
换句话说,如果你把功能了协议定义的像ProtocolB,那么你可以通过铸造对象的协议访问的默认实现.另一方面,如果将函数保留在协议定义中,则无法转换为协议以获取默认协议行为.
将函数定义排除在协议之外似乎允许在行为方面具有最大的灵活性.
有什么缺点?如果从协议定义中取出该功能,您会失去什么?你是否失去了任何功能?
声明协议中的函数指示编译器在调用函数时使用动态调度,因为编译器期望实现协议的类型为该函数提供实现.这被称为a method requirement.如果类型未定义方法,则运行时将方法调用解析为协议扩展中声明的方法.
仅在协议扩展中声明该函数告诉编译器他不需要使用动态调度,并使用静态调度,这更快,但在多态性方面不能很好地工作,因为协议扩展实现将是调用.
举例来说,让我们考虑以下代码:
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()
}
Run Code Online (Sandbox Code Playgroud)
上面的代码将有输出
This is a Circle
This is a Square
Run Code Online (Sandbox Code Playgroud)
这是因为draw()是a method requirement,意味着当draw()调用when时,运行时将搜索draw ()元素的实际类型内的实现,在本例中是Circle和Square.
现在,如果我们不声明draw方法要求,这意味着我们在协议声明中没有提到它
protocol Shape {
}
Run Code Online (Sandbox Code Playgroud)
然后编译器将不再使用动态分派,并将直接进入协议扩展中定义的实现.因此代码将打印:
This is a Shape
This is a Shape
Run Code Online (Sandbox Code Playgroud)
更多,如果我们向下转换将数组的元素转换为我们期望的类型,那么我们将得到静态绑定.这将打印This is a Circle
if let circle = shapes[0] as? Circle {
circle.draw()
}
Run Code Online (Sandbox Code Playgroud)
因为编译器现在能够告诉第一个元素shapes是a Circle,并且因为Circle有一个draw()方法它会调用那个元素.
这是Swift处理抽象类的方法:它为您提供了一种方法,您可以指定符合该协议的类型所期望的内容,同时允许这些方法的默认实现.
| 归档时间: |
|
| 查看次数: |
701 次 |
| 最近记录: |