С вашим изменением во втором примере, включив m
в определение протокола, которое инструктирует Swift использовать динамическую диспетчеризацию.Поэтому, когда вы вызываете p.m()
, он динамически определяет, переопределил ли объект реализацию метода по умолчанию.В этом конкретном примере это приводит к тому, что метод рекурсивно вызывает сам себя.
Но в первом примере, если метод не является частью определения протокола, Swift будет использовать статическую диспетчеризацию, а из-за p
имеет тип P
, он вызовет реализацию m
в P
.
В качестве примера рассмотрим, где метод не является частью определения протокола (и, следовательно, не находится в«таблица свидетелей протокола»):
protocol P {
// func method()
}
extension P {
func method() {
print("Protocol default implementation")
}
}
struct Foo: P {
func method() {
print(“Foo implementation")
}
}
Поскольку foo
является ссылкой P
и поскольку method
не является частью определения P
, он исключает method
изпротокол свидетеля таблицы и использует статическую диспетчеризацию.В результате следующее напечатает «Реализация протокола по умолчанию»:
let foo: P = Foo()
foo.method() // Protocol default implementation
Но если вы измените протокол, чтобы явно включить этот метод, оставив все остальное таким же, method
будет включен в протокол-свидетельtable:
protocol P {
func method()
}
Тогда следующее теперь выведет «Foo реализация», потому что хотя переменная foo
имеет тип P
, она будет динамически определять, имеет ли базовый тип, Foo
,переопределите этот метод:
let foo: P = Foo()
foo.method() // Foo implementation
Для получения дополнительной информации о динамической и статической диспетчеризации см. видео WWDC 2016 Понимание производительности Swift .