Семантика памяти при захвате значений непосредственно в замыканиях

emenegro спросил: 28 апреля 2018 в 09:45 в: swift

Недавно я прочитал о списках захвата в этой статье objc.io. Я думаю, что это отличный совет, и я начал его использовать.

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

И я понял, что можно даже захватить методы, а не только значения:

.subscribe(onNext: { [showErrorAlert, populate] result in
    switch result {
    case .success(let book):
        populate(book)
    case .error(_):
        showErrorAlert(L10n.errorExecutingOperation.localized)
    }
})

Я пытаясь найти некоторую документацию, связанную с этим способом захвата, но я не могу ее найти. Безопасна ли эта практика? Это соответствует обычным танцам [weak self], strongSelf = self внутри закрытия?

2 ответа

Есть решение
muvaaa ответил: 28 апреля 2018 в 11:21

Насколько безопасна эта практика? Является ли это равным обычным танцам [слабости], strongSelf = self внутри замыкания?

Да и нет - методы захвата объектов также сохраняют объект. Захваченный метод может получить доступ к чему-либо из экземпляра, поэтому имеет смысл, что он сохраняет его.

С другой стороны, захват свойства не сохраняет экземпляр.

Вот небольшой фрагмент, который вы можете вставить на игровой площадке, чтобы убедиться сами:

import UIKit
import PlaygroundSupportPlaygroundPage.current.needsIndefiniteExecution = trueclass A {
    let name: String
    init(name: String) {
        self.name = name
    }    func a() {
        print("Hello from \(name)")
    }    func scheduleCaptured() {
        print("Scheduling captured method")
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [a] in
            a()
        }
    }    func scheduleCapturedVariable() {
        print("Scheduling captured variable")
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) { [name] in
            print("Hello from \(name)")
        }
    }    deinit {
        print("\(name) deinit")
    }
}var a1: A? = A(name: "instance 1")
a1?.scheduleCapturedVariable()var a2: A? = A(name: "instance 2")
a2?.scheduleCaptured()a1 = nil
a2 = nil

Вывод:

Scheduling captured variable
Scheduling captured method
instance 1 deinit
Hello from instance 1
Hello from instance 2
instance 2 deinit

Вы можете видеть, что instance 2 не деинициализируется до тех пор, пока не будет снят захваченный блок, а instance 1 деинициализируется сразу после установки на нуль.

Stimorol ответил: 28 апреля 2018 в 11:24
  1. Захват методов экземпляра небезопасен, если вы не уверены, что ваше закрытие будет расположено до deinit (fe you absolute, чтобы оно вызывало ограниченное количество раз а затем последовательность всегда заканчивается). То же самое для нереактивных прецедентов.
  2. Метод фиксируется (и не может быть зафиксирован слабо), поэтому закрытие будет содержать ссылку, запрещая ARC уничтожать ее. Следовательно, при закрытии поведения strongSelf сохраняется только слабый ref для объекта, он привязывается к нему как сильный при запуске запуска, а затем освобождает сильную ссылку в конце выполнения. Литий> ол>