Есть ли потенциальные недостатки в использовании [слабого я] в закрытии после объявления сильного я? - PullRequest
0 голосов
/ 26 октября 2018

Название, которое я использовал для этого, может быть не совсем понятным, поэтому я надеюсь, что пример рабочего кода может дать некоторую ясность.

Проблема, с которой я сталкиваюсь, заключается в том, что у меня есть сценарий, похожий на код ниже:

import Foundation

class Test {
    private var isInner = false {
        didSet {
            print("isInner: \(isInner)")
        }
    }

    private func runClosure(closure: () -> ()) {
        closure()
    }

    func callClosure() {
        // Weak 1
        runClosure { [weak self] in
            self?.isInner = false
            guard let strongSelf = self else { return }
            // Can this [weak self] create problems?
            // Weak 2
            strongSelf.runClosure { [weak self] in
                self?.isInner = true
            }
        }
    }
}

let test = Test()
test.callClosure()
// The following is printed to the console
// isInner: false
// isInner: true

Все выше работает точно так, как задумано, и это хорошо.

Меня беспокоит второе использование [weak self]. В то время как self объявляется слабым в начале функции ( Слабый1 ), вскоре после этого я установил его на strongSelf.

Я мог бы повторно использовать мой более ранний strongSelf, но рассматриваемые функции на самом деле являются потенциально долго выполняющимися операциями, и существует вероятность того, что self может быть вне области действия между Weak1 и Weak2 .

Однако, до меня дошло, что существует вероятность того, что Weak2 будет недействительным, что я и надеюсь уточнить с этим вопросом.

В конечном счете, все, что делает слабый, это создает необязательную переменную для себя, поэтому я не знаю ни о каких потенциальных ловушках. Кроме того, Weakself1 , strongSelf и Weakself2 все указывают на один и тот же адрес памяти во время выполнения callClosure().

1 Ответ

0 голосов
/ 29 октября 2018

Давайте рассмотрим шаг за шагом (строка за строкой)

// Weak 1
runClosure { [weak self] in

В первой строке создается ссылка на целевой объект, ссылка которого по совпадению (или нет) называется self.

    self?.isInner = false

В приведенной выше строке используется слабая ссылка, она не влияет на жизненный цикл целевого объекта.

    guard let strongSelf = self else { return }

Теперь эта строка действительно создает сильную ссылку на целевой объект, что продлит срок службы объектов как минимум на strongSelf. Теперь, в зависимости от агрессивности компилятора, strongSelf может умереть после прохождения этой строки (последняя ссылка в коде) или после завершения выполнения внешнего замыкания.

    // Can this [weak self] create problems?
    // Weak 2
    strongSelf.runClosure { [weak self] in

Теперь это имеет почти тот же эффект, что и захват с внешней крышки. Он создает ссылку на, возможно, уже освобожденный экземпляр (в зависимости от того, * strongSelf все еще жив или нет).

            self?.isInner = true

Это обычное необязательное использование, не влияющее на целевое время жизни.

Теперь, при условии, что runClosure работает асинхронно, нет никаких проблем с целевым объектом, живущим больше, чем ожидалось (при условии, что больше нет сильных ссылок).

Подводя итог, можно сказать, что время жизни объекта определяется количеством сильных ссылок на этот объект. Как только все сильные ссылки будут уничтожены, объект будет автоматически освобожден. В вашем конкретном сценарии внутреннее замыкание слабо фиксирует и уже является слабой ссылкой, и это не влияет на жизненный цикл целевого объекта. Единственный игрок - strongSelf, который уничтожается не позднее уничтожения внешнего закрытия.

...