Какие проблемы возникают при использовании делегатов и их иерархии в iOS?
Что проверяют
Понимаете ли вы, как делегат живёт в памяти и где у паттерна кончаются возможности. Первое, что хотят услышать, это weak и почему протокол должен быть классовым. Следом спрашивают, чем заменить делегат, когда слушателей несколько или цепочка стала слишком длинной.
Как отвечать
Основные проблемы:
- цикл сильных ссылок: контроллер держит view, view держит контроллер как делегата. Решение
weak var delegate: SomeDelegate?, а дляweakпротокол объявляется какprotocol SomeDelegate: AnyObject; - обратная сторона
weak: уUITableViewсвойстваdelegateиdataSourceслабые. Если вынести data source в отдельный объект и не сохранить его в свойство, он освободится сразу, и таблица останется пустой без единой ошибки; - один слушатель: делегат это связь один к одному. Нескольким подписчикам нужен multicast-делегат на
NSHashTable.weakObjects(), Combine илиNotificationCenter; - цепочки проброса: ячейка сообщает контроллеру списка, тот родителю, тот координатору, и каждое звено заводит протокол ради проброса. Здесь проще замыкание или событие напрямую в координатор;
- опциональные методы:
@objc optionalтянет Objective-C рантайм, а реализация по умолчанию вextensionпротокола коварна: опечатка в сигнатуре у класса не даст ошибки компиляции, молча будет вызываться пустая реализация; - потоки:
URLSessionDelegateпо умолчанию вызывается на очереди сессии, а не на главном потоке. В Swift 6 со строгой проверкой конкурентности такие места всплывают ошибками изоляции, UI-протоколы помечают@MainActor.
Альтернативы называйте с ценой: замыкания требуют [weak self], Combine и AsyncStream добавляют управление подпиской.
Где спотыкаются
Ставят unowned вместо weak, чтобы избавиться от опционала. Если делегат освободится раньше, приложение упадёт.
Говорят, что делегат передаёт данные только в одну сторону. Метод делегата может возвращать значение, как tableView(_:heightForRowAt:), это обычный двусторонний контракт.
Предлагают заменить все делегаты на NotificationCenter. Связь становится неявной, и найти, кто реагирует на событие, можно только поиском по имени уведомления.