Правильно ли использовать `[слабое я]` в быстром закрытии?
Я всегда использую [weak self]
в быстром закрытии, чтобы предотвратить референсный цикл. Вот код ниже, это правильный путь?
someTask(completion: {[weak self] (result) in
if self == nil {
return
}
//is it safe when reach here?
self!.xxx = yyy
self!.doLongTermWork()
self!.finish() //will crash when self is nil?
})
Слабая личность не удерживает сильную власть над экземпляром. Так когда self.doLongTermWork()
, будут self
быть настроенным на nil
опять где-то еще?
3 ответа
Ваша модель имеет расы. Если self
был освобожден в то же самое время, когда выполнялось закрытие вашего обработчика завершения, это могло привести к сбою. Как правило, избегайте использования !
Оператор принудительной распаковки, если можете.
Я склонялся бы к
guard
Шаблон "раннего выхода" (сокращение вложенных скобок, облегчение чтения кода). Стандартное решение Swift 4.2:someTask { [weak self] result in guard let self = self else { return } self.xxx = yyy self.doLongTermWork() self.finish() }
До Swift 4.2, в котором реализован SE-0079, нам нужно было сделать что-то вроде:
someTask { [weak self] result in guard let strongSelf = self else { return } strongSelf.xxx = yyy strongSelf.doLongTermWork() strongSelf.finish() }
Вы можете понять, почему мы предпочитаем улучшение Swift 4.2, так как
strongSelf
синтаксис неэлегатный.Другая очевидная альтернатива просто:
someTask { [weak self] result in self?.xxx = yyy self?.doLongTermWork() self?.finish() }
Иногда вам нужен "слабый сам - сильный сам танец" (первые две альтернативы), но, похоже, это не так. Это, вероятно, достаточно.
Есть другие сценарии / крайние случаи, которые можно рассмотреть, но это основные подходы.
Вы сказали:
someTask(completion: {[weak self] (result) in
if self == nil {
return
}
//is it safe when reach here?
self!.xxx = yyy
})
Нет! Вы не сохранили self
так что в теории это может стать nil
в любое время во время выполнения закрытия. Это, вероятно, не будет, но "вероятно" не достаточно хорош. И восклицательные знаки всегда являются приглашением к краху.
Сделайте слабый-сильный танец и сделайте это правильно:
someTask(completion: {[weak self] (result) in
if let self = self { // or let `self` before Swift 4
// here, self is safe, because you made the reference strong again
self.xxx = yyy
}
})
Вы можете использовать это как Swift 4.2
someTask(completion: {[weak self] (result) in
guard let self == self { return }
//it safe when reach here always
self.xxx = yyy
self.doLongTermWork()
self.finish()
})