Диспетчеризация методов — это алгоритм, используемый для выбора соответствующего метода, который должен быть вызван при обращении. Основная цель диспетчеризации методов — предоставить программе информацию о том, где в памяти можно найти исполняемый код для конкретного метода.
Компилируемые языки имеют три типа диспетчеризации методов:
Статическая диспетчеризация — самый быстрый метод диспетчеризации в Swift. Поскольку переопределение метода недоступно, существует только одна реализация метода, и она находится в одном месте в памяти.
\ Мы можем использовать статическую диспетчеризацию с помощью ключевых слов, таких как static, final, private.
\ Статическая диспетчеризация является методом диспетчеризации по умолчанию для типов значений, поскольку типы значений не могут быть переопределены.
\ Давайте рассмотрим несколько примеров:
Когда мы добавляем ключевое слово final к классу, его методы не поддерживают переопределение, и именно тогда вступает в игру статическая диспетчеризация.
// MARK: Final class final class ClassExample { // MARK: Static dispatch func method() { // implementation ... } }
Когда вы добавляете реализацию протокола по умолчанию с помощью расширения, его метод диспетчеризации переключается на статическую диспетчеризацию вместо использования Таблицы свидетелей.
// MARK: Prorocol Extension extension ProtocolExample { // MARK: Direct Dispatch func method() { // implementation ... } } class ClassExample2: ProtocolExample {} let classExample2 = ClassExample2() classExample2.method()
Когда метод реализован в расширении, это означает, что он не может быть переопределен подклассами. В этом случае есть место для статической диспетчеризации.
// MARK: Example Class Extension class ClassExample3 {} extension ClassExample3 { // MARK: Direct Dispatch func method() { // implementation ... } } let classExample3 = ClassExample3() classExample3.method()
Мы не можем получить доступ к приватному методу за пределами тела класса. Это означает, что метод не может быть переопределен и использует статическую диспетчеризацию.
// MARK: Access Control class ClassExample4 { // MARK: Direct Dispatch private func method() { // implementation ... } }
Табличная диспетчеризация используется, когда нам приходится иметь дело с наследованием. Это тип диспетчеризации по умолчанию, используемый в Swift.
Для каждого экземпляра класса или подкласса создается виртуальная таблица, которая содержит информацию о реализованных методах для каждого класса и хранит ссылку на соответствующую реализацию. Основным недостатком диспетчеризации виртуальной таблицы является то, что она имеет более низкую скорость, чем статическая диспетчеризация.
\ Давайте рассмотрим пример:
// MARK: Virtual Table class ParentClass { func method1() {} func methdod2() {} } class ChildClass: ParentClass { override func method1() {} func method3() {} }
\ Для каждого экземпляра создается своя виртуальная таблица следующим образом:
\
Таблица свидетелей используется протоколами и создается для каждого класса, соответствующего протоколу. ЦП использует эту таблицу, чтобы определить, где следует искать соответствующую реализацию. Каждый тип (значение и ссылка), соответствующий протоколу, имеет свою собственную Таблицу свидетелей протокола, которая содержит указатели на методы типа, требуемые протоколом.
\ Давайте рассмотрим пример:
// MARK: Witness Table Dispatch protocol ProtocolExample { func method1() func method2() } class ClassExample1: ProtocolExample { func method1() {} func method2() {} } class ClassExample2: ProtocolExample { func method1() {} func method2() {} }
\ В этом случае для каждого класса создается таблица свидетелей:
\
Диспетчеризация сообщений — это наиболее динамичный стиль диспетчеризации методов. Он ищет соответствующую реализацию во время выполнения. Поскольку он работает во время выполнения, мы можем использовать Method Swizzling для изменения реализаций методов.
\ Если вы хотите использовать диспетчеризацию сообщений, вам нужно добавить @objc dynamic перед реализацией метода.
// MARK: Message Dispatch class ClassExample: NSObject { @objc dynamic func method() {} } class SubClassExample: ClassExample { @objc dynamic override func method() {} } let subclass = SubClassExample() subclass.method()
\ Реализация метода ищется в SubClassExample. Если в этом классе нет реализации этого метода, поиск продолжается в родительском классе и так далее, пока не достигнет NSObject.
\
Давайте объединим все типы в одну таблицу:
\
В итоге, диспетчеризация методов в Swift является критическим аспектом выполнения кода, влияющим на производительность и гибкость. Выбирая правильный метод диспетчеризации, разработчики могут оптимизировать свой код, обеспечить адаптивность и эффективно использовать динамические функции Swift. Понимание и освоение диспетчеризации методов необходимо для создания эффективных и адаптивных приложений Swift.


