Почему в расширениях протоколов в Swift нельзя хранить свойства
Что проверяют
Понимаете ли вы, что ограничение не стилистическое, а следствие модели памяти и раздельной компиляции. Второй слой: знаете ли вы легальный способ добавить состояние и цену обходных путей.
Как отвечать
Хранимое свойство это место в памяти экземпляра, то есть размер и раскладка типа. Раскладка фиксируется там, где тип объявлен. Расширение может лежать в другом модуле, который соберут позже, а экземпляры к тому моменту уже создаются по старой раскладке. Поэтому хранимых свойств экземпляра не добавляет ни одно расширение, не только протокольное.
У протокола причина ещё жёстче: собственной раскладки у него нет. Его принимают класс, структура и перечисление, у каждого свой размер, а код расширения компилируется один раз на всех. Хранить значение негде.
Разрешены вычисляемые свойства, методы, сабскрипты, вложенные типы и реализации по умолчанию. Состояние добавляют через требование в самом протоколе, а хранение оставляют конформящему типу:
protocol Cacheable {
var cache: [String: Data] { get set }
}
extension Cacheable {
mutating func store(_ data: Data, for key: String) {
cache[key] = data
}
}Про обходные пути надо говорить честно. Ассоциированные объекты работают только для классов, видимых в среде выполнения Objective-C, требуют ручной политики владения и не дают потокобезопасности, а глобальный словарь по идентификатору объекта течёт, пока его не чистить руками. Для структур не годится ни то, ни другое.
Где спотыкаются
Объясняют запрет чистотой протокола как контракта. Протокол давно не только контракт, у него есть реализации по умолчанию, а ограничение чисто техническое, про раскладку памяти.
Путают расширение протокола с расширением обычного типа. Статическое хранимое свойство в расширении класса или структуры объявить можно, это частый приём для констант, а в расширении протокола нельзя: у каждого конформящего типа такое хранилище должно быть своим.
Забывают mutating у метода расширения, который пишет в свойство, и ловят ошибку компиляции. Обратная крайность не лучше: протокол ограничивают ссылочными типами лишь бы не писать mutating, и теряют возможность применить его к структуре.