В приведенном вами примере [weak self]
потенциально не требуется.Это зависит от того, что вы хотите, если ViewModel
будет выпущено до завершения запроса.
Как отмечено в URLSessionDataTask
документах (выделено мной):
После созданиязадача, вы запускаете ее, вызывая ее метод resume ().Затем сеанс поддерживает строгую ссылку на задачу до тех пор, пока запрос не завершится или не завершится с ошибкой ;вам не нужно поддерживать ссылку на задачу, если только она не полезна для внутренней бухгалтерии вашего приложения.
Сессия имеет четкую ссылку на задачу.Задача имеет сильную ссылку на закрытие.Закрытие имеет сильную ссылку на ViewModel
.Пока ViewModel
не имеет четкой ссылки на задачу (чего нет в коде, который вы предоставили), цикл отсутствует.
Вопрос в том, хотите ли вы убедиться, чтоViewModel
продолжает существовать достаточно долго для выполнения замыкания.Если вы делаете (или не заботитесь), то вы можете использовать простую сильную ссылку.Если вы хотите помешать задаче сохранить ViewModel
в действии, вам следует использовать слабую ссылку.
Именно так вы должны думать о ссылочных циклах.Не существует общего правила «используйте weak
здесь».Вы используете weak
, когда вы это имеете в виду;когда вы не хотите, чтобы это закрытие сохраняло self
, пока оно не будет выпущено.Это особенно верно, если это создает цикл.Но нет общего ответа на вопрос «это создает цикл».Это зависит от того, какие части содержат ссылки.
Это также указывает на то, где ваш текущий дизайн API не так хорош, как мог бы быть.Вы проходите didLoadData
в init
.Скорее всего, это создаст эталонные циклы и заставит вашего абонента использовать weak
.Если вместо этого вы сделали didLoadDdata
обработчиком завершения на loadUser()
, то вы могли бы избежать этой проблемы и упростить жизнь вызывающей стороне.
func loadUser(completion: @escaping ((User?) -> Void)? = nil) {
service.fetchUser {
self?.user = user
didLoadData?(user)
}
}
(Ваш текущий API имеет условие гонки в любом случае.Вы запускаете loadUser()
до того, как можно установить didLoadData
. Возможно, вы предполагаете, что обработчик завершения не завершит работу до того, как вы установили dataDidLoad
, но на самом деле это не обещает. Возможно, это правда, но этов лучшем случае хрупкий.)