Reputation: 6971
I've recently read about capture lists in this article of objc.io. I think it is a great tip and I'm started to use it.
Although it is not totally clarified, I assume there are no retain cycles when capturing this way, so you get a captured strong reference but with no retain cycle to be worried about.
And I've realized that it is possible to even capture methods, not only values:
.subscribe(onNext: { [showErrorAlert, populate] result in
switch result {
case .success(let book):
populate(book)
case .error(_):
showErrorAlert(L10n.errorExecutingOperation.localized)
}
})
I am trying to find some documentation related to this way of capturing but I cannot find any. Is this practice safe? Is this equal to the usual dance of [weak self]
, strongSelf = self
inside the closure?
Upvotes: 1
Views: 83
Reputation: 197
deinit
(f.e. you absolutely sure it will trigger limited amount of times and then sequence always ends). Same for non-reactive use-cases. strongSelf
behavior closure keep only weak ref to object, binds it as strong on execution start, and then releases strong reference at the execution end. Upvotes: 1
Reputation: 600
Is this practice safe? Is this equal to the usual dance of [weak self], strongSelf = self inside the closure?
Yes and no - capturing methods of objects retains the object as well. The captured method could be accessing anything from the instance so it makes sense that it retains it.
On the other hand capturing a property does not retain the instance.
Here is a short snippet you can paste in playground to see for yourself:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class 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
The output is:
Scheduling captured variable
Scheduling captured method
instance 1 deinit
Hello from instance 1
Hello from instance 2
instance 2 deinit
You can see that instance 2
is not deinitialised until the captured block is fired, while instance 1
is deinitialised immediately after set to nil.
Upvotes: 1