Возможно ли хранить слабые ссылки непосредственно в массиве Swift без дополнительной обёртки?
Нет. Элемент Array хранится как обычное значение, поэтому ссылка на объект внутри массива будет сильной; модификатор weak нельзя применить к отдельному элементу коллекции. Для слабой коллекции используют объект-обёртку со свойством weak либо специализированную коллекцию.
ARC управляет временем жизни объектов через владение ссылками: обычные ссылки являются сильными, а weak-ссылки не увеличивают счётчик сильных ссылок и автоматически обнуляются. Такая модель хорошо работает для свойств объектов, но Array хранит элементы как значения и не предоставляет отдельного ownership-квалификатора для каждого элемента.
Поэтому слабость ссылки должна быть описана в специально выделенном хранилище — например, в свойстве класса-обёртки. Сам массив при этом может сильно владеть обёртками, не продлевая жизнь целевых объектов.
Запись объектов в массив обычного ссылочного типа продлевает их жизнь. Если массив используется как список наблюдателей, делегатов или зарегистрированных экранов, это может привести к тому, что удалённые из приложения объекты останутся живы из-за единственной сильной ссылки в массиве.
Замена элементов на Optional проблему не решает: Array<Item?> хранит сильные ссылки на Item, когда значения не равны nil. Требуется именно отдельное слабое хранилище для каждой ссылки.
Типичный подход — хранить в массиве экземпляры WeakBox, а внутри каждого экземпляра объявлять слабое свойство:
Массив сильно удерживает WeakBox, но WeakBox слабо ссылается на Item. После освобождения последней сильной ссылки на Item ARC уничтожает объект и обнуляет value во всех слабых хранилищах.
У такого решения есть обязательное следствие: массив может содержать уже пустые обёртки. При обходе коллекции нужно пропускать элементы, у которых value == nil, а периодически их удалять, если это необходимо для ограничения размера массива.
Обёртка должна быть ссылочным типом. Если сделать её значением, копирование элемента коллекции потребует отдельного анализа семантики слабого хранилища и может привести к ограничениям языка или неожиданному поведению. На практике класс-обёртка явно отделяет владение контейнером от владения целевым объектом.
Контроллеры экранов регистрируются в диспетчере событий. Вариант с массивом сильных ссылок прост, но контроллеры не освобождаются после закрытия экранов. Вариант с массивом Optional не меняет ситуацию, потому что ссылки всё равно сильные.
Использование массива WeakBox<Controller> устраняет удержание контроллеров: диспетчер владеет только обёртками, а контроллеры могут быть уничтожены независимо. При отправке события диспетчер извлекает value, пропускает nil и удаляет пустые обёртки во время обслуживания коллекции.
Плюс подхода — прозрачная интеграция с обычными Swift-коллекциями. Минусы — дополнительный объект на каждую ссылку, необходимость очистки устаревших элементов и отсутствие автоматической потокобезопасности: синхронизацию массива нужно организовать отдельно.
1. Делает ли Array<Item?> ссылки слабыми?
Нет. Optional описывает возможность отсутствия значения, но не владение ссылкой. Если Item? содержит экземпляр класса, массив сильно удерживает этот экземпляр. Слабым должно быть само хранилище ссылки, например свойство weak var value: Item? внутри обёртки.
2. Может ли слабая коллекция содержать nil сразу после добавления объекта?
Да. Обёртка не владеет объектом, поэтому объект должен иметь другую сильную ссылку в момент добавления и после него. Если сильная ссылка исчезла до чтения элемента коллекции, ARC может уничтожить объект, а слабое свойство обёртки будет автоматически равно nil.
3. Гарантирует ли слабая обёртка безопасный одновременный доступ из нескольких потоков?
Нет. weak управляет владением и обнулением ссылки, но не делает операции с массивом атомарными и не защищает его от одновременной модификации. Доступ к самой коллекции и логика удаления пустых обёрток должны быть защищены подходящим механизмом синхронизации или ограничены одним потоком исполнения.