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 we need to explicitly use capture list for weak variables in swift closure?
Sort of, in this specific example, but you need to be very careful about how you think about what's happening.
First, yes, this is identical. We can tell that by generating the SIL (swiftc -emit-sil main.swift
). Except for the difference in the name of self
vs weakSelf
, these generate exactly the same unoptimized SIL. In order to make it even clearer, I'll change the name of the variable in the capture list (this is just a renaming, it doesn't change the behavior):
weakSelf
weak var weakSelf = self
callback = {
weakSelf?.perform()
}
weak_self
callback = { [weak weakSelf = self] in
weakSelf?.perform()
}
Compare them
$ swiftc -emit-sil weakSelf.swift > weakSelf.sil
$ swiftc -emit-sil weak_self.swift > weak_self.sil
$ diff -c weakSelf.sil weak_self.sil
*** weakSelf.sil 2022-03-27 10:58:13.000000000 -0400
--- weak_self.sil 2022-03-27 11:01:22.000000000 -0400
***************
*** 102,108 ****
// Foo.init()
sil hidden @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo {
! // %0 "self" // users: %15, %8, %7, %2, %22, %1
bb0(%0 : $Foo):
debug_value %0 : $Foo, let, name "self", argno 1, implicit // id: %1
%2 = ref_element_addr %0 : $Foo, #Foo.callback // user: %4
--- 102,108 ----
// Foo.init()
sil hidden @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo {
! // %0 "self" // users: %8, %7, %15, %2, %22, %1
bb0(%0 : $Foo):
debug_value %0 : $Foo, let, name "self", argno 1, implicit // id: %1
%2 = ref_element_addr %0 : $Foo, #Foo.callback // user: %4
Except for the order of the users comment for self
, they're identical.
But be very, very careful with what you do with this knowledge. This happens to be true because there exists a strong reference to self
elsewhere. Going down this road too far can lead to confusion. For example, consider these two approaches:
// Version 1
weak var weakObject = Object()
let callback = {
weakObject?.run()
}
callback()
// Version 2
var weakObject = Object()
let callback = { [weak weakObject] in
weakObject?.run()
}
callback()
These do not behave the same at all. In the first version, weakObject
is already released by the time callback
is created, so callback()
does nothing. The compiler will generate a warning about this, so in most cases this is an unlikely bug to create, but as a rule you should generally do weak captures in the capture list so that it occurs as close to the closure creation as possible, and won't accidentally be released unexpectedly.
When to use [self] vs [weak self] in swift blocks?
[self]
indicates that self
is intentionally held with a strong reference (and so some syntax is simplified). [weak self]
indicates that self
is held with a weak reference.
why would I use strong capture
[self]
inside block as there are chances of memory leak
You would use this when you know there is no reference cycle, or when you wish there to be a temporary reference cycle. Capturing self
does not by itself create a cycle. There has to be a cycle. You may know from your code that there isn't. For example, the thing holding the closure may be held by some other object (rather than self
). Good composition (and decomposition of complex types into smaller types) can easily lead to this.
Alternately, you may want a temporary cycle. The most common case of this URLSessionTask. The docs are very valuable here (emphasis added):
After you create a task, you start it by calling its resume() method. The session then maintains a strong reference to the task until the request finishes or fails; you don’t need to maintain a reference to the task unless it’s useful for your app’s internal bookkeeping.
Another common example is DispatchQueue, which similarly holds onto a closure until it finishes. At that point, it releases it, killing the cycle and allowing everything to deallocate. This is useful and powerful (and common!), when used with intent. It's a source of bugs when used accidentally. So Swift requires you to state your intentions and tries to make the situation explicit.
When you build your own types that retain completion handlers, you should strongly consider this pattern, too. After calling the completion handler, set it to nil
(or {_ in }
for non-optionals) to release anything that completion handler might be referencing.
One frustrating effect of the current situation is that developers slap [weak self]
onto closures without thought. That is the opposite of what was intended. Seeing self
was supposed to cause developers to pause and think about the reference graph. I'm not certain it ever really achieved this, but as a Swift programmer you should understand that this is the intent. It's not just random syntax.
Weird weak self and retain cycle behaviour
First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.
Let's try to walk through them one by one.
Printer1
As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action
instance method as the closure to the Delayer, and since all closures are reference types, passing self.action
will retain its surrounding scope (which is Printer1).
Printer2
Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.
Printer3
Here, a retain cycle is not created, because the self.weakAction
property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2
.
Printer4
First, you're capturing a weak reference to self, and then fetching welf?.action
and passing the result into Delayer. Again, welf?.action
is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action
instance method will retain self. This is identical to Printer1
.
Printer5
Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self
is never directly referenced in the passed closure, it will not capture self
in that scope, only the welf
weak reference. This is pretty much identical to Printer2
, but with a slightly different syntax.
Personally, I would opt for the Printer2
way (creating a new closure, retaining a weak reference to self and using that to call self?.action
). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.
Is it the right way using `[weak self]` in swift closure?
Your pattern has race condition. If self
was deallocated at the exact same time as your completion handler closure was executing, it could crash. As a general rule, avoid using the !
forced unwrapping operator if you can.
I’d lean towards the
guard
“early exit” pattern (reducing nested braces, making code easier to read). The standard Swift 4.2 solution is:someTask { [weak self] result in
guard let self = self else { return }
self.xxx = yyy
self.doLongTermWork()
self.finish()
}Before Swift 4.2, which implemented SE-0079, we would have to do something like:
someTask { [weak self] result in
guard let strongSelf = self else { return }
strongSelf.xxx = yyy
strongSelf.doLongTermWork()
strongSelf.finish()
}You can see why we prefer the Swift 4.2 improvement, as this
strongSelf
syntax is inelegant.The other obvious alternative is just:
someTask { [weak self] result in
self?.xxx = yyy
self?.doLongTermWork()
self?.finish()
}Sometimes you need the “weak self - strong self dance” (the first two alternatives), but it would not appear to be the case here. This is likely sufficient.
There are other scenarios/edge cases that one might contemplate, but these are the basic approaches.
Related Topics
Subclass Nsapplication in Swift
What Is the Role of Avcapturedevicetype.Builtindualcamera
Uitesting Xcode 7: How to Tell If Xcuielement Is Visible
Avspeechutterance - Swift - Initializing with a Phrase
How Do Closures Capture Values from Previous Calls
Iterating with for .. in on a Changing Collection
Peak Detection for Growing Time Series Using Swift
How to Get User Input in Apple's Swift Language in a Command Line Tool
Do You Need to Release Cgcontextref in Swift
Need to Check That Braces in Given Array Are Balanced or Not
Understanding the Removerange(_:) Documentation
The File "Xxx.Mp4" Couldn't Be Opened Because You Don't Have Permission to View It
How to Set an Attributed Title Color for State in Swift
Why Does an @Objc Enum Have a Different Description Than a Pure Swift Enum