Приведи примеры табличной и прямой диспетчеризации
Что проверяют
Знаете ли вы, как Swift выбирает, какой код выполнить, и понимаете ли последствия. Тема всплывает, когда переопределение внезапно не срабатывает или когда обсуждают скорость, поэтому ждут не определений, а примеров, где вид диспетчеризации меняет поведение.
Как отвечать
Прямая диспетчеризация это когда адрес функции известен на этапе компиляции. Так вызываются методы структур и перечислений, методы с final и private, свободные функции и методы расширений протокола, которых нет в списке требований. Вариант самый быстрый, его можно встроить в место вызова, но переопределить такой метод нельзя.
Табличная диспетчеризация это вызов через таблицу указателей. У классов это таблица виртуальных методов: у каждого метода, объявленного в теле класса, свой номер ячейки, наследник кладёт туда свою реализацию. У протоколов ту же роль играет таблица соответствия, куда попадают объявленные требования.
class Base { func run() {} } // таблица
final class Fast { func run() {} } // прямая
struct Point { func move() {} } // прямаяТретий вид стоит назвать самому: вызов через среду выполнения Objective-C. Он включается для того, что помечено @objc dynamic и видно этой среде. Вариант медленнее остальных, но именно на нём держатся подмена реализации и наблюдение за значением свойства.
Где спотыкаются
Называют табличной диспетчеризацией словарь, где ключам сопоставлены замыкания. Это приём в коде, а не механизм языка: таблицу строит компилятор, и обращение идёт по номеру ячейки, а не по строке.
Второе: приводят как пример прямой диспетчеризации базовый класс с переопределённым методом и массив наследников. Всё наоборот, это ровно табличный вызов. Прямым он станет, если пометить класс final.
Третье: забывают про расширения. Метод, объявленный в расширении класса, в таблицу не попадает и вызывается напрямую, поэтому переопределить его в наследнике не выйдет. С протоколами ловушка того же рода: если метод есть только в расширении протокола, реализация выбирается по типу переменной, и у значения, лежащего в переменной протокольного типа, сработает версия из расширения.