Ответ
Message Dispatch. Метод ищут в runtime Objective-C по селектору. У класса для этого есть свой список, в каждой записи селектор и адрес нужного метода. Runtime идет по списку класса, потом по списку родителя, пока не найдет селектор. Нашли селектор, из этой же записи берут адрес метода и вызывают его.
Селектор это уникальное имя метода в runtime. Грубо говоря, это не строка с именем, а его номер. Одинаковые имена получают один номер. Сравнить два селектора значит проверить, что номер совпал.
Достоинства:
- Реализацию можно менять во время выполнения. Например, method swizzling: у селектора в списке класса подменяют адрес тела метода.
- На этом стоят KVO, Target-Action и вызов по
#selector. - После первого поиска селектор кэшируют. Следующий вызов с тем же именем уже не обходит весь список методов.
Недостатки:
- Самый медленный вид диспетчеризации. Даже с кэшем это тяжелее прямого вызова и virtual table.
- Нужен runtime Objective-C. Для метода Swift это
@objc dynamicу наследникаNSObject.
Так работают:
- Методы с
@objc dynamicу наследниковNSObject. Даже вызов из Swift идет по селектору. - Target-Action. Например, кнопка вызывает метод по
#selector. - KVO у свойств
@objc dynamic. Можно подписаться на изменение свойства, runtime сам вызовет код, когда значение поменяется.
Message dispatch ищет метод во время выполнения через objc_msgSend. Так работают @objc, наследники NSObject и dynamic. Поиск идёт по классу и его родителям, поэтому метод можно переопределить и даже подменить, как в swizzling.
Это самый гибкий и самый дорогой из трёх способов. Компилятор почти не встраивает такой вызов: адрес заранее неизвестен.
- плюс: переопределение, KVO и подмена метода
- минус: поиск при каждом вызове и почти нет inlining