How to Capture Multiple Arguments Weakly in a Swift Closure

In Swift, if I have a closure capturing [weak self], is it good practice to unwrap the optional self at the beginning of the closure?

I believe that the first implementation is better because self could become nil between the statements

And that's why the second implementation is in fact better! If self is not nil at the first statement, the first statement makes it so that self couldn't become nil between the statements. It retains self exactly for the remainder of the block. This is called "the weak–strong dance".

guard let self = self else { return }
// ^^^^ this self is _strong_, not weak; it retains self

Do capture lists of inner closures need to redeclare `self` as `weak` or `unowned`?

The [weak self] in anotherFunctionWithTrailingClosure is not needed.

You can empirically test this:

class Experiment {
func someFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}

func anotherFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}

func doSomething() {
print(#function)
}

func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}

// go ahead and add `deinit`, so I can see when this is deallocated

deinit {
print("deinit")
}
}

And then:

func performExperiment() {
DispatchQueue.global().async {
let obj = Experiment()

obj.testCompletionHandlers()

// sleep long enough for `anotherFunctionWithTrailingClosure` to start, but not yet call its completion handler

Thread.sleep(forTimeInterval: 1.5)
}
}

If you do this, you will see that doSomething is never called and that deinit is called before anotherFunctionWithTrailingClosure calls its closure.

That having been said, I might still be inclined to use the [weak self] syntax on anotherFunctionWithTrailingClosure to make my intent explicit.

Weak reference to closure in Swift

Is it possible to make the closure references weak in my Observable class

No. Only class instances can be referred to via weak references in Swift, and a function is not a class instance. (And not only must they be class instances, they must be an Optional wrapping a class instance.)

There are some pretty obvious ways around this, or course - the simplest being a wrapper class. But I do not actually recommend that in this situation, because you have not convinced me that weak references to functions are needed here in the first place. Remember, a weak reference to an object to which there is no strong reference will instantly lose the reference and will be pointing at nil. I can't believe that is what you want. I think you're barking up a wrong tree here.

Should function arguments be weak?

Weak references aren't something you should use "just because I see a closure".

Even with closures, and even with self, there are plenty of times where you don't want a weak reference.

Weak references exist to break strong reference cycles. That's it. The captured variables of closures are one kind of strong reference that exists. If a closure strongly captures a variable, nothing scary happens.

The issue is when a closure captures foo, and then you store the closure inside the foo. That's a strong reference cycle, which needs to be broken. One of the two references needs to be weak, so either you should hold the closure weakly in the foo, or you should capture the foo weakly in the clsoure.

Your persons has type Array<Person>. There could be a strong reference cycle if one of those Person objects has a strong reference to helloView, which itself has a strong reference to its didHelloPressed closure. But that would be very weird, and is almost certainly (hopefully!) not what you're doing, so no, there's no need to weakly capture persons.

Even more, depending on the relationship between helloView and what self is in this context, it's entirely possible that you might want to capture self strongly, too.



Related Topics



Leave a reply



Submit