5. Два вызова logMessage: свой тип и протокол

Какая реализация logMessage() сработает в каждом из двух вызовов? Что будет в консоли и почему?

protocol Logger {} extension Logger { func logMessage() { print("Logging from base logger.") } } struct FeatureLogger: Logger { func logMessage() { print("Logging from feature.") } } let featureLogger = FeatureLogger() let logger: Logger = featureLogger featureLogger.logMessage() logger.logMessage()

Дополнительные вопросы

1. Можно ли изменить код так, чтобы два раза вывелось Logging from feature?

Темы

Компании

Ответ

featureLogger.logMessage() // Logging from feature. logger.logMessage() // Logging from base logger.

Сначала рассмотрим структуру и вызов ее метода.

struct FeatureLogger: Logger { func logMessage() { print("Logging from feature.") } } let featureLogger = FeatureLogger() featureLogger.logMessage()

Хоть структура и соответствует протоколу Logger, вызов идет не через тип протокола. Поэтому используется статическая диспетчеризация. Компилятор уже знает, какую реализацию вызвать, и программа делает прямой вызов метода структуры.

print выведет: Logging from feature.

Теперь рассмотрим часть с протоколом.

protocol Logger {} extension Logger { func logMessage() { print("Logging from base logger.") } } let logger: Logger = featureLogger logger.logMessage()

Вызов метода уже идет через тип протокола. Казалось бы, диспетчеризация должна быть через witness table. Но посмотрим внимательнее.

Функция logMessage() объявлена в extension к протоколу, но не объявлена в самом протоколе. Это не требование протокола, поэтому метод не попадет в witness table.

Значит, при этом вызове будет статическая диспетчеризация: logMessage() через тип протокола выполнит базовую реализацию из extension.

print выведет: Logging from base logger.

Дополнительные вопросы

1. Чтобы в обоих случаях вывелось Logging from feature., второй вызов нужно перевести на динамическую диспетчеризацию через witness table. Для этого достаточно добавить logMessage() в требования протокола. Тогда вызов через тип протокола возьмет реализацию структуры, т.к. метод у FeatureLogger уже есть, и он попадет в таблицу.

protocol Logger { func logMessage() } featureLogger.logMessage() // Logging from feature. logger.logMessage() // Logging from feature.