Why Swift closure not capture self?
There are retain cycles in both cases. The difference is the nature of the reference, not the place where closure
is set. This difference is manifested in what it takes to break the cycle:
- In the "inside" situation, the reference inside the closure is
self
. When you release your reference toa
, that is insufficient to break the cycle, because the cycle is directly self-referential. To break the cycle, you would have had also to seta.closure
tonil
before settinga
tonil
, and you didn't do that.
- In the "outside" situation, the reference is
a
. There is a retain cycle so long as youra
reference is not set tonil
. But you eventually do set it tonil
, which is sufficient to break the cycle.
(Illustrations come from Xcode's memory graph feature. So cool.)
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
Why is declaring self not required in structures where it's required in classes?
The purpose of including self
when using properties inside an escaping closure (whether optional closure or one explicitly marked as @escaping
) with reference types is to make the capture semantics explicit. As the compiler warns us if we remove self
reference:
Reference to property 'someProperty' in closure requires explicit use of 'self' to make capture semantics explicit.
But there are no ambiguous capture semantics with structs. You are always dealing with a copy inside the escaping closure. It is only ambiguous with reference types, where you need self
to make clear where the strong reference cycle might be introduced, which instance you are referencing, etc.
By the way, with class types, referencing self
in conjunction with the property is not the only way to make the capture semantics explicit. For example, you can make your intent explicit with a “capture list”, either:
Capture the property only:
class SomeClass {
var someProperty = 1
func someMethod(completion: @escaping () -> Void) { ... }
func anotherMethod() {
someMethod { [someProperty] in // this captures the property, but not `self`
print(someProperty)
}
}
}Or capture
self
:class SomeClass {
var someProperty = 1
func someMethod(completion: @escaping () -> Void) { ... }
func anotherMethod() {
someMethod { [self] in // this explicitly captures `self`
print(someProperty)
}
}
}
Both of these approaches also make it explicit what you are capturing.
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.
Closure cannot implicitly capture self parameter. Swift
It's because you're using struct
. Since structs are value, they are copied (with COW-CopyOnWrite) inside the closure for your usage. It's obvious now that copied properties are copied by "let" hence you can not change them. If you want to change local variables with callback you have to use class
. And beware to capture self weakly ([weak self] in
) to avoid retain-cycles.
Why do closures require an explicit `self` when they're all non-escaping by default in Swift 3?
In Swift 3, all closures are non-escaping by default
No, in Swift 3, only closure function arguments (i.e function inputs that are functions themselves) are non-escaping by default (as per SE-0103). For example:
class A {
let n = 5
var bar : () -> Void = {}
func foo(_ closure: () -> Void) {
bar = closure // As closure is non-escaping, it is illegal to store it.
}
func baz() {
foo {
// no explict 'self.' required in order to capture n,
// as foo's closure argument is non-escaping,
// therefore n is guaranteed to only be captured for the lifetime of foo(_:)
print(n)
}
}
}
As closure
in the above example is non-escaping, it is prohibited from being stored or captured, thus limiting its lifetime to the lifetime of the function foo(_:)
. This therefore means that any values it captures are guaranteed to not remain captured after the function exits – meaning that you don’t need to worry about problems that can occur with capturing, such as retain cycles.
However, a closure stored property (such as bar
in the above example) is by definition escaping (it would be nonsensical to mark it with @noescape
) as its lifetime not limited to a given function – it (and therefore all its captured variables) will remain in memory as long as the given instance remains in memory. This can therefore easily lead to problems such as retain cycles, which is why you need to use an explicit self.
in order to make the capturing semantics explicit.
In fact, case in point, your example code will create a retain cycle upon viewDidLoad()
being called, as someClosure
strongly captures self
, and self
strongly references someClosure
, as it's a stored property.
It's worth noting that as an extension of the "stored function properties are always escaping" rule, functions stored in aggregates (i.e structures and enumerations with associated values) are also always escaping, as there's no restrictions on what you do with such aggregates. As pointed out by pandaren codemaster, this currently includes Optional
– meaning that Optional<() -> Void>
(aka. (() -> Void)?
) is always escaping. The compiler might eventually make this a special case for function parameters though, given that optional is already built on a lot of compiler magic.
Of course, one place where you would expect to be able to use the @noescape
attribute is on a closure that’s a local variable in a function. Such a closure would have a predictable lifetime, as long as it isn’t stored outside of the function, or captured. For example:
class A {
let n = 5
func foo() {
let f : @noescape () -> Void = {
print(n)
}
f()
}
}
Unfortunately, as @noescape
is being removed in Swift 3, this won't be possible (What's interesting is that in Xcode 8 GM, it is possible, but yields a deprecation warning). As Jon Shier says, we’ll have to wait for it to be re-added to the language, which may or may not happen.
Swift, how much of self do closures capture?
class B who is only referenced from class A.
I assume you mean something like this in A
:
var objB: B?
Just before closure Z calls functions from class B, class B gets cleaned out.
Well, there is only one way to clean out B
:
objB = nil
Now when you call B's methods in the closure, you will get an "unexpectedly found nil while unwrapping an optional value".
This is because you only captured self
, which is A
,in the closure. You did not capture B
at all. The instance of B
is retained by the strong reference from A
. Basically, B
is not captured by the closure.
My confusion comes because in closure Z, i can specify [weak self], but I cannot do that for the functions I want to call in class B.
Well, you can capture B
though. Just like this:
[b = self.objB] in ...
You can also make it weak:
[weak b = self.objB] in ...
This way you can also capture B in the closure.
Related Topics
Use of Undeclared Type 'Attributedstring'
For-In Loop and Type Casting Only for Objects Which Match Type
Why Is the Swift Compiler Marking This as an Error
Swift 2.0 'Inout' Function Parameters and Computed Properties
How to Rotate Sprites Around a Joint
How to Make a Extension for Array of Specific Type in Swift
What Makes a Property a Computed Property in Swift
How to Call the More Specific Method of Overloading
What Would Be a Proper Storyboard Example of Combining Nav Bars and Tab Bars in One App
How to Embed Third Party Framework on Ionic Capacitor Custom Plugin
Swift 3 String Contains Exact Sentence/Word
How to Speed Up Updating Relationship Among Tables, After One or Both Tables Are Already Saved
Session.Datataskwithurl Completionhandler Never Called
Filter Realm Objects to Only Get One (Distinct) Object by Attribute
How to Demonstrate a Zombie Object in Swift