Confused on closure strong reference cycle? - swift

class HTMLElement {
let name : String
let text: String?
//Declaring a lazy variable that has a strong reference to this closure
lazy var asHTML: Void -> String = {
//Optional binding here
if let text = self.text {
return "<\(self.name)>\(text)<\(self.name)>"
} else {
return "<\(self.name) >"
}
}
init(name: String, text: String? = nil){
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
My Question is: Why is the closure declared Lazy, I know it has something to do with self not being known in the closure, but isn't that the same case for the init method where self hasn't been created?
Secondly,Where exactly is the strong reference cycle in this code example, is it self that strongly references to asHTML, if so where is the second part of the strong referencing that causes the cycle?
Third, Why is the constant text property an optional when constants cannot change value(from nil to a value and back to nil)?
Lastly, What does it mean to have the parameter text: String? = nil in the init method when the init method is used to accept parameters sent by the user?
Sorry for this long question, I'm just confused on the closure strong reference cycle....although I do understand strong reference cycles between class properties and class instances.

1
lazy is used for attributes that are only created when called upon. So before you call myClass.myLazyAttribute it will not take up any space. This also means that it will init after the class has initialised, which can be very useful.
In this case lazy is used to get access to self, like you stated, because self is not available until the instance has been initialised.
2
The apple doc from where code is.
Closures capture the values used in them. In this case it captures self.
It does not create a Strong Reference Cycle between class A and class B, but between itself and a closure. It makes a lot more sense if you imagine that the operation inside the closure takes a very long time. During the execution something else has happened and you want to deinit the instance. But the closure has captured self and it will keep the instance alive until it is done.
By using [unowned self] in you can again deinit the instance while the closure is running. Although this will crash your app.
good info on this specific use : link
In the specific case of a closure, you just need to realize that any variable that is referenced inside of it, gets "owned" by the closure. As long as the closure is around, those objects are guaranteed to be around. The only way to stop that ownership, is to do the [unowned self] or [weak self].
What a Strong Reference Cycle in essence is:
you have an instance of a class
the instance has a reference count higher than 0
there is no longer a reference to the instance available to your program.
Or even shorter: the reference count of the instance is higher than the number of accessible references.
In this case the reference count of self goes up by 1 because it is captured by the closure. We can not access that reference because we can not say something like: closure.selfAttribute, so we can not set that to nil. Only when the closure is finished will the reference count go down by 1 again.
3
It is an optional constant, but it's initial value is set in the init method of the class. So it can receive a value in the init method, but it will be immutable. This is called a late init.
4
This is a function parameter with a default value.
func someFunction(myParamWithDefaultValue : Int = 10) {
print(myParamWithDefaultValue)
}
someFunction() // 10
someFunction(5) // 5

Related

Swift Reference Cycle When Capturing A Computed String Property Declared From Inside An Instance Method

I'm implementing an in-game store using Swift and SpriteKit. There is a class called Store which has a method setupItems() inside of which we declare and instantiate instances of a class StoreItem and also add each store item instance as a child of Store. Each StoreItem has an optional closure property called updateInventory which is set inside of setupItems() as well. This is an optional closure because some items don't have a limited inventory.
Store also has an unowned instance property storeDelegate which is responsible for determining how funds are deducted and how storeItems are applied once purchased. storeDelegate is unowned as it has an equal or greater lifetime than Store.
Now, here is where things get interesting - the closure updateInventory references a computed string variable called itemText which makes a calculation based on a property of storeDelegate. If itemText is declared and instantiated as a variable inside of setupItems() we have a reference cycle and store is not deallocated. If instead, itemText is declared and instantiated as an instance property of Store (right under the property unowned storeDelegate that it references) then there is no reference cycle and everything deallocates when it should.
This seems to imply that referencing storeDelegate from a computed variable inside an instance method of a class doesn't respect the unowned qualifier. Code examples follow:
Current Scenario
protocol StoreDelegate: AnyObject {
func subtractFunds(byValue value: Int)
func addInventoryItem(item: InventoryItem) throws
var player: Player! { get }
}
class Store: SKSpriteNode, StoreItemDelegate {
unowned var storeDelegate: StoreDelegate
init(storeDelegate: StoreDelegate) {
self.storeDelegate = storeDelegate
setupItems()
...
}
func setupItems() {
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem = StoreItem(name: "coolItem")
storeItem.updateInventory = {
[unowned storeItem, unowned self] in
// some logic to check if the update is valid
guard self.storeDelegate.player.canUpdate() else {
return
}
storeItem.label.text = itemText
}
}
The above leads to a reference cycle, interestingly if we move
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
outside of updateItems and make it an instance variable of Store right below unowned var storeDelegate: StoreDelegate, then there is no reference cycle.
I have no idea why this would be the case and don't see mention of it in the docs. Any suggestions would be appreciated and let me know if you'd like any additional details.
storeItem.updateInventory now keeps strong reference to itemText.
I think the issue is that itemText holds a strong reference to self implicitly in order to access storeDelegate, instead of keeping a reference to storeDelegate. Anothe option is that even though self is keeping the delegate as unowned, once you pass ot to itemText for keeping, it is managed (ie, strong reference again).
Either way, you can guarantee not keeping strong reference changing itemText to a function and pass the delegate directly:
func setupItems() {
func itemText(with delegate: StoreDelegate) -> String
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem...
storeItem.updateInventory = {
// ...
storeItem.label.text = itemText(with self.storeDelegate)
}
}

Is it safe to capture properties of `self` without capturing `self`?

You can copy this playground verbatim:
var closures=[() -> Void]()
class Thing {
let name: String
init(_ name: String) { self.name=name }
}
class RefThingWrapper {
let thing: Thing
init(_ t: Thing) {
self.thing=t
closures.append { [thing, weak self] in // Even though `thing` captured strongly, `self` could still get deallocated.
print((self == nil) ? "`self` deallocated" : "`self` not deallocated")
print(thing.name) // Decided to use `thing` within closure to eliminate any chance of optimizations affecting the test results — which I found when I captured `self` strongly without using it.
}
}
}
struct ValThingWrapper {
let thing: Thing
init(_ t: Thing) {
self.thing=t
closures.append { [weak thing] in
print((thing == nil) ? "`thing` deallocated" : "`thing` not deallocated")
}
}
}
var wrapper=Optional(RefThingWrapper(Thing("Thing 1"))) // Test with reference type.
//var wrapper=Optional(ValThingWrapper(Thing("Thing 1"))) // Test with value type.
closures[0]()
wrapper=nil
closures[0]()
It demonstrates how a property of self — whether self is a reference or value type — can be captured within a closure independently of self. Running the program as is demonstrates a captured property existing after self has been deallocated. Testing with the value type wrapper demonstrates that, if weakly captured, the instance will be deallocated once the referencing value instance is deallocated.
I wasn't sure this was possible because when creating the closure at first, I forgot to initialize the property I was capturing. So the compiler complained — in the capture list — 'self' used before all stored properties are initialized. So I figured self was being captured implicitly, and only after digging deeper discovered otherwise.
Is this documented somewhere? I found this post by Joe Groff where he proposes:
For 'let' properties of classes, it'd be reasonable to propose having
closures capture the property directly by default in this way
instead of capturing 'self' (and possibly allowing referencing them
without 'self.', since 'self' wouldn't be involved in any cycle formed
this way).
This was back in 2015, and I didn't find any implemented proposals that arose from the discussion. Is there any authoritative source that communicates this behavior?
If you’re just asking for documentation on capture lists and reference types, see The Swift Programming Language Resolving Strong Reference Cycles for Closures. Also see the Language Reference: Capture Lists
If your capture list includes value type, you’re getting copy of the value.
var foo = 1
let closure = { [foo] in
print(foo)
}
foo = 42
closure() // 1; capturing value of `foo` as it was when the closure was declared
If your capture list includes a reference type, you’re getting a copy of the reference to that current instance.
class Bar {
var value: Int
init(value: Int) { self.value = value }
}
var bar = Bar(value: 1)
let closure = { [bar] in
print(bar.value)
}
bar.value = 2
bar = Bar(value: 3)
closure() // 2; capturing reference to first instance that was subsequently updated
These captured references are strong by default, but can optionally be marked as weak or unowned, as needed, too.
That Capture Lists document outlines the way that you can capture a property without capturing self:
You can also bind an arbitrary expression to a named value in a capture list. The expression is evaluated when the closure is created, and the value is captured with the specified strength. For example:
// Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }
I’m not crazy about their code sample, but it illustrates the capture of a property without capturing self, nonetheless.

Why doesn't changing a closure property to a function cause the same retain cycle?

A strong reference cycle can also occur if you assign a closure to a
property of a class instance, and the body of that closure captures
the instance.
If closures as properties can cause strong retain cycles, and functions are a type of closure according to the Swift programming guide:
Global and nested functions, as introduced in Functions, are actually
special cases of closures
Why doesn't changing a closure property to an instance method cause the same retain cycle?
Instance methods are functions that belong to instances of a
particular class, structure, or enumeration.
For example, as is, the code below does not deallocate the instance of HTMLElement, however, if I change asHTML to a function instead of a name for a closure expression, the HTMLElement is deallocated.
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
return "<\(self.name)>\(self.text ?? "")</\(self.name)>"
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {print("\(name) is being deinitialized")}
}
var headerTitle: HTMLElement? = HTMLElement(name: "h1", text: "Welcome")
print(headerTitle!.asHTML())
headerTitle = nil
Debugger:
<h1>Welcome</h1>
When changed to a function:
func asHTML() -> String {
return "<\(self.name)>\(self.text ?? "")</\(self.name)>"
}
Debugger:
<h1>Welcome</h1>
h1 is being deinitialized
A closure causes a retain cycle when you save it to an instance variable, so you have a strong reference to the closure, and then the closure has a strong reference to the object that holds it.
In your first example, your var asHTML is a closure saved to an instance variable in your HTMLElement class. That means that the HTMLElement instance keeps a strong reference to the closure.
Then in your closure you make reference to self. That means that the closure holds a strong reference to the object that's defining it (again, the HTMLElement instance.) You've created a retain cycle.
Now, if instead you have a function asHTML() that you call, you're not saving the function as an object somewhere. It's an instance method of the object. It has a reference to self because it is PART of self. Thus no retain cycle.

Are closures allocated memory when they are defined or first called?

Are closures allocated memory when they are defined or first called? For example, is the closure for the name asHTML allocated memory when the HTMLElement named headerTitle is instantiated, or on the next line when asHTML() is called. Furthermore, with classes a deinit implementation is provided. Is there a similar feature for closures?
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
return "<\(self.name)>\(self.text ?? "")</\(self.name)>"
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {print("\(name) is being deinitialized")}
}
var headerTitle: HTMLElement? = HTMLElement(name: "h1", text: "Welcome")
print(headerTitle!.asHTML())
headerTitle = nil
In general, a closure will need to be allocated at its definition site in order to capture variables used within the closure that are outside of the closure. However, the lazy keyword makes it so that the closure is not declared until its first use. From Apple's documentation
A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.
I'm assuming what you mean by deinit for closures mean to release any strong references to reference-semantic variables in your closure. Your example comes from Apple's own The Swift Programming Guide (Swift 4). They explain that this example has a strong reference cycle, meaning the class instance holds a strong reference to the closure, and the closure has a strong reference to the instance. And nether are willing to release one another causing the instance to never be de-initialized. Here is Apple's visual:
If you read look further ahead in the chapter, they speak about defining a capture list to break the strong reference cycle.
lazy var asHTML: () -> String = {
[unowned self] in /*<-- Add self in the capture list, so the closure does not have a strong reference to the instance. */
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
Now when headerTitle is set to nil, the deinit print message will show.

Why Swift closure not capture self?

I was testing swift closure with Xcode playground.
This is my code:
import UIKit
class A{
var closure: ()->() = {}
var name: String = "A"
init() {
self.closure = {
self.name = self.name + " Plus"
}
}
deinit {
print(name + " is deinit")
}
}
var a: A?
a = A()
a = nil
As what is expected, a is self contained by closure, so a is never released.
But, when I add this line before the last line:
a?.closure = { a?.name = "ttt" }
Then, I found "A is deinit" in the output window, which means a is released.
Why? is a not recycle reference?
To be test, I use a function to set the closure, which the code is version 2:
import UIKit
class A{
var closure: ()->() = {}
func funcToSetClosure(){
self.closure = { self.name = "BBB"}
}
var name: String = "A"
init() {
self.closure = {
self.name = self.name + " Plus"
}
}
deinit {
print(name + " is deinit")
}
}
var a: A?
a = A()
a?.funcToSetClosure()
a = nil
Again, a is never released.
So I got the conclusion, when closure is set by init or a function in the class, it will cause recycle reference, when it is set out side the class, it will not cause recycle reference. Am I right?
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 to a, that is insufficient to break the cycle, because the cycle is directly self-referential. To break the cycle, you would have had also to set a.closure to nil before setting a to nil, and you didn't do that.
In the "outside" situation, the reference is a. There is a retain cycle so long as your a reference is not set to nil. But you eventually do set it to nil, which is sufficient to break the cycle.
(Illustrations come from Xcode's memory graph feature. So cool.)
As the SIL documentation says, when you capture a local variable in a closure, it will be stored on the heap with reference counting:
Captured local variables and the payloads of indirect value types are
stored on the heap. The type #box T is a reference-counted type that
references a box containing a mutable value of type T.
Therefore when you say:
var a : A? = A()
a?.closure = { a?.name = "ttt" }
you do have a reference cycle (which you can easily verify). This is because the instance of A has a reference to the closure property, which has a reference to the heap-allocated boxed A? instance (due to the fact that it's being captured by the closure), which in turn has a reference to the instance of A.
However, you then say:
a = nil
Which sets the heap-allocated boxed A? instance's value to .none, thus releasing its reference to the instance of A, therefore meaning that you no longer have a reference cycle, and thus A can be deallocated.
Just letting a fall out of scope without assigning a = nil will not break the reference cycle, as the instance of A? on the heap is still being retained by the closure property of A, which is still being retained by the A? instance.
What causes the retain cycle is that you reference self in the closure.
var a: A?
a = A()
a?.closure = { a?.name = "ttt" }
a = nil
You change the closure to no longer reference self, that's why it is deallocated.
In the final example, you make it reference self again in the closure, that is why it does not deallocate. There are ways around this, this post is a great list of when to use each case in swift: How to Correctly handle Weak Self in Swift Blocks with Arguments
I would imagine you are looking for something like this, where you use a weak reference to self inside the block. Swift has some new ways to do this, most commonly using the [unowned self] notation at the front of the block.
init() {
self.closure = { [unowned self] in
self.name = self.name + " Plus"
}
}
More reading on what is going on here: Shall we always use [unowned self] inside closure in Swift