Что выведется в консоли при вызове двух одинаковых функций в протоколе с табличной диспетчеризацией
Что проверяют
Понимаете ли вы, чем требование протокола отличается от метода, который живёт только в расширении. Табличная диспетчеризация идёт через витнес-таблицу, и в неё попадает не всё, что можно вызвать у типа. Вопрос почти всегда дают куском кода на бумаге: смотрят, читаете ли вы код глазами компилятора.
Как отвечать
Сначала правило: в витнес-таблицу протокола попадают только методы, объявленные в самом протоколе. Метод, который есть лишь в расширении протокола, требованием не является и диспетчеризуется статически, по типу переменной в точке вызова.
protocol Greeter {
func hello() // требование, идёт в витнес-таблицу
}
extension Greeter {
func hello() { print("extension hello") }
func bye() { print("extension bye") }
}
struct Person: Greeter {
func hello() { print("Person hello") }
func bye() { print("Person bye") }
}
let p: Greeter = Person()
p.hello() // Person hello
p.bye() // extension bye
Person().bye() // Person byeДальше объясняете вывод одной фразой: hello ищется в таблице и находит реализацию типа, bye подставляется компилятором по статическому типу Greeter. Полезно добавить третий случай: у классов есть ещё таблица виртуальных методов и наследование, а пометка @objc dynamic переводит вызов на рантайм Objective-C.
Где спотыкаются
Отвечают «вызовется реализация типа», не посмотрев, объявлен ли метод в протоколе. Это верно ровно для требований, а вся суть вопроса в том, что второй метод требованием не объявлен.
Считают, что всё решает тип значения. Для bye решает тип переменной: одно и то же значение через let p: Greeter и через let p = Person() печатает разное. Реализация в структуре тут не переопределяет метод расширения, а затеняет его.
Путают табличную диспетчеризацию с динамической в смысле Objective-C. Вызов через таблицу косвенный, но адрес берётся по фиксированному индексу, поиска по селектору нет, и подмена метода в рантайме на такой вызов не действует.