Что такое метод диспетчеризации и какие типы диспетчеризации существуют
Что проверяют
Понимаете ли вы, как Swift выбирает реализацию метода и как это влияет на скорость и поведение кода. Вопрос иногда путают с GCD, но речь не о DispatchQueue, а о method dispatch. Следом почти всегда дают задачку с методом в расширении протокола.
Как отвечать
Способов три, от быстрого к гибкому:
- статическая (direct): адрес функции известен при компиляции, вызов можно заинлайнить. Так работают методы
structиenum, методы классов сfinalилиprivate,static-методы и методы из расширений протоколов, не объявленные в самом протоколе; - табличная: у класса есть виртуальная таблица (vtable) с указателями на реализации, наследник подменяет свои записи. Так вызываются переопределяемые методы классов. Для протоколов аналог это witness table: требование протокола через
any Pили обобщённый параметр вызывается через неё; - message dispatch: механизм рантайма Objective-C через
objc_msgSend, поиск метода по селектору в момент вызова. Включается модификаторомdynamicвместе с@objc, именно он нужен для KVO и method swizzling.
При этом @objc сам по себе только делает метод видимым из Objective-C, а Swift-код продолжает звать его через vtable.
Что влияет на выбор на практике: final и private разрешают компилятору вызывать напрямую, Whole Module Optimization сама выводит final для internal-классов без наследников, а дженерики и some P компилятор может специализировать и превратить табличный вызов в прямой. У any P такой возможности нет.
Классическая задача: в extension P есть func greet(), в протоколе этого метода нет, а struct S: P определяет свой greet(). Вызов через let p: any P = S() напечатает версию из расширения: записи в witness table у такого метода нет, и выбор делается статически по типу переменной.
Где спотыкаются
Отвечают про очереди GCD и DispatchQueue.main.async. Это другая тема.
Считают, что @objc включает message dispatch. Без dynamic нет ни KVO на Swift-свойстве, ни свизлинга.
Пытаются переопределить в наследнике метод, объявленный в расширении класса. Без @objc компилятор этого не даст: такие методы диспетчеризуются статически.