Правильно ли использовать `[слабое я]` в быстром закрытии?

Я всегда использую [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 был освобожден в то же самое время, когда выполнялось закрытие вашего обработчика завершения, это могло привести к сбою. Как правило, избегайте использования ! Оператор принудительной распаковки, если можете.

  1. Я склонялся бы к guard Шаблон "раннего выхода" (сокращение вложенных скобок, облегчение чтения кода). Стандартное решение Swift 4.2:

    someTask { [weak self] result in
        guard let self = self else { return }
    
        self.xxx = yyy
        self.doLongTermWork()
        self.finish()
    }
    
  2. До 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 синтаксис неэлегатный.

  3. Другая очевидная альтернатива просто:

    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()
})
Другие вопросы по тегам