Что такое runtime в Objective-C
Что проверяют
Понимаете ли вы, что вызов метода в Objective-C это отправка сообщения, разрешаемая во время выполнения, а не прямой переход по адресу функции. На этом механизме держатся KVO, категории, архивация и подмена реализаций, и в iOS вопрос обычно ведёт именно туда.
Как отвечать
Runtime это библиотека libobjc и структуры, в которых живут классы: указатель isa, список методов, протоколы, поля, кэш выборок. Компилятор превращает отправку сообщения в вызов objc_msgSend с получателем, селектором и аргументами. По isa находится класс, в кэше или списке методов ищется реализация и вызывается.
Если метод не найден, работает цепочка: сначала resolveInstanceMethod:, затем forwardingTargetForSelector:, затем methodSignatureForSelector: вместе с forwardInvocation:, и только после этого приложение падает с unrecognized selector.
Что стоит назвать из практики:
- подмена реализаций через method_exchangeImplementations, динамическое добавление методов и классов;
- связанные объекты, которыми в категорию добавляют хранилище;
- KVO подменяет isa объекта на скрытый подкласс, поэтому object_getClass и метод class отвечают по-разному;
- сообщение к nil безопасно и возвращает нулевое значение;
- метаданные о типах полей и свойств, на которых стоят KVC и архивация.
Где спотыкаются
Говорят, что Objective-C динамически типизирован. Типы проверяет компилятор, динамична именно диспетчеризация, а id лишь отключает проверку в конкретном месте.
Считают performSelector: универсальной заменой вызову. У него жёсткие ограничения по числу и типам аргументов, ARC не знает сигнатуру и предупреждает о возможной утечке; для нетривиальных случаев берут NSInvocation или заранее полученный указатель на реализацию.
Свизлят методы как попало: без единственной точки установки, без вызова исходной реализации, в категории вместо родного класса. Две библиотеки, подменившие один метод, дают порядок, зависящий от порядка загрузки, и плавающие падения, которые не воспроизводятся под отладчиком.