typography
Диспетчеризация
Что это вообще такое? Какие виды бывают?

Как связаны диспетчеризация и наследование?

Как работает статическая и динамическая диспетчеризация?

Чем отличается Table dispatch от message dispatch?

Кто обрабатывает message dispatch?

Зачем нужен final и как он работает?

Что такое Whole module optimization?

Static Dispatch
Метод статически отправляется, если он не может быть переопределен.

Этот тип отправки метода является самым простым и быстрым из всех отправлений. Причина его скорости заключается в том, что, учитывая, что функции не могут быть переопределены, подразумевается, что существует только одна реализация этого метода, которая будет храниться где-то в памяти во время выполнения. Среда выполнения может напрямую перейти к этому адресу памяти и выполнить его (в отличие от других отправлений, как вы увидите).

Вы всегда можете указать метод для использования статической отправки, используя следующие ключевые слова:

• static
• final

С другой стороны, это также метод отправки по умолчанию для функций, которые объявлены в типах значений, поскольку по определению типы значений не могут быть переопределены.
V-Table Dispatch
Это метод отправки по умолчанию, используемый в Swift для ссылочных типов.

В этом методе во время компиляции создается таблица поиска, в которой указывается фактическая реализация метода, который необходимо вызвать во время выполнения. Во время выполнения эта таблица поиска хранится в виде массива адресов фактического местоположения в памяти, где находится реализация.
Причина, по которой эта отправка является методом отправки по умолчанию для ссылочных типов, заключается в том, что классы должны поддерживать наследование. V-таблица помогает с унаследованными классами, генерируя правильные вызовы переопределенных методов и неперекрываемых методов.
Поэтому, если у вас есть настройка, подобная приведенной ниже, среда выполнения определяет, когда ей нужно вызвать реализацию в классе дочернего класса, а не в классе суперкласса:
import Foundation

class SuperClass {
    func overrideMethod() {}
    func nonOverridenMethod() {}
}

class ChildClass: SuperClass {
    override func overrideMethod() {}
}

let superObj = SuperClass()
superObj.overrideMethod()
superObj.nonOverridenMethod()

let childObj = ChildClass()
childObj.overrideMethod()
childObj.nonOverridenMethod()
Из сгенерированного выше кода вы можете видеть, что как Дочерний класс, так и СуперКласс имеют все методы, объявленные доступными в их V-таблицах.
Также обратите внимание, что идентификатор метода (строка, которая выглядит как @$SilGen10SuperClass..., появляющаяся в конце каждой строки) одинаков для nonOverriddenMethod для обоих классов (поскольку дочерний класс не переопределяет его), но отличается для объявления overrideMethod (поскольку они имеют разные реализации).
Если вы хотите узнать больше о том, как выполняются фактические вызовы, вы можете сгенерировать SIL с помощью следующей команды:
sil_vtable SuperClass {
  #SuperClass.overrideMethod: (SuperClass) -> () -> () : @$s6SilGen10SuperClassC14overrideMethodyyF	// SuperClass.overrideMethod()
  #SuperClass.nonOverridenMethod: (SuperClass) -> () -> () : @$s6SilGen10SuperClassC18nonOverridenMethodyyF	// SuperClass.nonOverridenMethod()
  #SuperClass.init!allocator: (SuperClass.Type) -> () -> SuperClass : @$s6SilGen10SuperClassCACycfC	// SuperClass.__allocating_init()
  #SuperClass.deinit!deallocator: @$s6SilGen10SuperClassCfD	// SuperClass.__deallocating_deinit
}

sil_vtable ChildClass {
  #SuperClass.overrideMethod: (SuperClass) -> () -> () : @$s6SilGen10ChildClassC14overrideMethodyyF [override]	// ChildClass.overrideMethod()
  #SuperClass.nonOverridenMethod: (SuperClass) -> () -> () : @$s6SilGen10SuperClassC18nonOverridenMethodyyF [inherited]	// SuperClass.nonOverridenMethod()
  #SuperClass.init!allocator: (SuperClass.Type) -> () -> SuperClass : @$s6SilGen10ChildClassCACycfC [override]	// ChildClass.__allocating_init()
  #ChildClass.deinit!deallocator: @$s6SilGen10ChildClassCfD	// ChildClass.__deallocating_deinit
}
Message Dispatch
Это забавно, хотя и может привести к некоторому снижению производительности, поэтому важно понимать, когда его использовать! В предыдущем разделе мы видели, как отправка V-таблицы может быть использована для переноса решения о том, какая реализация метода должна быть вызвана во время выполнения, но у нас есть другая проблема. Чтобы понять эту проблему, нам нужно будет вернуться в историю.
Objective-C - это очень зависимый от времени выполнения язык. Что я имею в виду под этим утверждением?

Это означает, что Objective-C предоставил нам возможности, с помощью которых вы могли бы выполнять множество проверок кода и изменять реализации кода во время выполнения.
Язык обычно откладывает многие свои задачи на время выполнения, чтобы ускорить время компиляции.
Было много функций, которые это включало во время выполнения, например:
Проверка принадлежности экземпляра к определенному типу класса с помощью функции isMemberOfClass. Или, если вы хотите проверить, принадлежит ли он определенному классу в его иерархии наследования, вы можете использовать isKindOfClass.
Проверка того, может ли класс реагировать на определенный метод с помощью функции Responsestoselector
Динамическое изменение реализаций методов во время выполнения с использованием чего-то, называемого методом swizzling

Добавление реализаций методов во время выполнения с помощью class_addMethod
И их гораздо больше. Все это происходит благодаря библиотеке, называемой библиотекой времени выполнения Objective-C, встроенной в Objective-C, которую использует NSObject. Учитывая, что почти каждый класс в Objective-C должен был наследовать от этого класса, эти функции были доступны почти везде.

Теперь мы знаем, что Objective-C является очень гибким языком во время выполнения и что вы можете существенно изменять реализации методов, добавлять реализации методов и т.д., И все это во время выполнения. Это может создать проблему для способа отправки V-таблицы, поскольку генерация V-таблицы происходит во время компиляции. V-таблица, сгенерированная во время компиляции, может не представлять правильную реализацию метода, который необходимо вызвать, поскольку он мог быть удален или новые методы могли быть добавлены во время выполнения.

Отправка сообщений - это решение вышеуказанных проблем, когда во время выполнения поддерживается другая таблица. Во время фактического вызова во время выполнения в этой таблице выполняется поиск, чтобы определить фактический адрес метода для выполнения вызова.
Методы, использующие динамическую диспетчеризацию, не включаются в V-таблицу, сгенерированную во время компиляции, поскольку такие методы могут быть заменены во время выполнения из-за возможностей среды выполнения Objective-C.

Это метод отправки по умолчанию в Objective-C для всех элементов, которые необходимо динамически отправлять.
динамический и @objc
Этот раздел посвящен сгенерированному SIL коду в несколько более глубоком смысле. Единственная теория, которая может быть предложена здесь, заключается в следующем: ключевое слово dynamic используется в Swift для включения динамической отправки для методов, которым это необходимо (например, в методе swizzling или в коде, связанном с KVO), тогда как @objc используется для предоставления этого метода Objective-C.
До Swift 3 все динамические элементы автоматически предоставлялись Objective-C, но начиная с Swift 4 вы должны явно указывать как dynamic, так и @objc перед методами, которые вам нужно предоставить для динамической отправки.
В Swift методы, которые необходимо использовать в качестве объектов селектора, должны быть объявлены с помощью @objc по той причине, что эти методы все еще должны быть доступны Objective-C. Механизм целевого действия, который используется здесь, по-прежнему написан на Objective-C.
Например, когда вы добавляете наблюдателей уведомлений или методы к UIBarButton, созданному в коде (с использованием механизма целевого действия и т.д.), Эти методы используют возможности среды выполнения Obj-C для вызова этих методов.
С этого момента это будет небольшое погружение в сгенерированный SIL код, и это будет не очень красиво. Давайте возьмем пример простого класса:
Вывод
В целом, я хочу, чтобы вы убрали из этой статьи три типа отправлений и связанные с ними компромиссы.
Если целью чтения этой статьи была просто подготовка к собеседованиям или просто понимание теории, то, вероятно, нет необходимости углубляться в SIL, хотя есть совершенно другой технический анализ, который можно провести с помощью сгенерированного SIL кода, который лучше всего выполнить, фактически сгенерировав этот код в вашей системе.