I can see many examples of retain cycles in Swift. However many of them are incorrect, and reading the documentation does not make really simple examples that I can follow.
For the example:
class Dog {
func bark() {
print ("YAP")
}
}
var dog = Dog()
let doSomething = {
dog.bark()
}
doSomething()
Does the closure doSomething cause a retain cycle? I understand that the closure will execute quickly, but that is not the question. Does this inherently cause a retain cycle?
There is no retain cycle in the program you posted.
What is a retain cycle?
Consider each object (including each closure) in your program as one vertex in a directed graph. Consider a strong reference from object (or closure) A to object (or closure) B as an edge from A to B in the graph.
A retain cycle is a cycle in the graph: a path containing at least one edge (strong reference) that leads from a vertex back to itself.
For example, a typical retain cycle looks like this:
A view controller always has a strong reference to its view (if the view has been loaded). In this example, the view controller created a closure. The closure captured (has a strong reference to) the view controller. The view controller then stored the closure in a property on the view, creating a retain cycle.
What is the retain graph of your program?
Here's the retain graph of your program:
There are no retain cycles in this graph.
Here is another "example" that runs outside of playground to showcase a retain cycle with a closure:
class ClosureClassStrongRefCycle {
let str : String
lazy var printStr: () -> () = { // self is captured
print("\(self.str)")
}
init(str: String) {
self.str = str
}
deinit {
print("ClosureClassStrongRefCycle is being deallocated")
}
}
Now when you call your class like this :
do {
let myClassWithClosureStrongCycle = ClosureClassStrongRefCycle(str: "closure class strong cycle")
myClassWithClosureStrongCycle.printStr()
} // 'myClassWithClosureStrongCycle' is NEVER deallocated -> Strong reference cycle between this class and it's closure
the instance of ClosureClassStrongRefCycle retains itself because the closure within it retains self
finally if you want to get rid of the retain cycle, you can add unowned self like this :
lazy var printStr: () -> () = { [unowned self] in // Unowned copy of self inside printStr
print("\(self.str)")
}
Related
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)
}
}
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?
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.
Capturing instance methods isn't safe unless you're sure your closure will be disposed before deinit (f.e. you absolutely sure it will trigger limited amount of times and then sequence always ends). Same for non-reactive use-cases.
The method is captured strongly(and can't be captured weakly), so the closure would keep the reference, prohibiting ARC from destroying it. Hence with 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.
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
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
I am having hard time figuring out how to make sure when to use [weak self]/[unowned self] in the closure body. In the two scenarios shown below, according to me, it depends upon if the class B owns the passed closure or not.
Now If the implementation of class B is hidden I am not really sure how to decide on using [weak self]/[unowned self].
Can someone please help me understand how you will decide ?
/******** Scenario 1 **********/
class A {
var b:B?
let p = "Some Property of A"
init() {
print("Init of A")
self.b = B(closure: { (number) -> Void in
print(self.p) // capturing self but still no need to write [weak/unowned self]
print(number)
})
}
deinit {
print("Deinit of A")
}
}
// Suppose this is a library class whose implementation is hidden
class B {
init(closure:(Int->Void)) {
print("Init of B")
// ... do some work here
closure(20)
}
deinit {
print("Deinit of B")
}
}
var a:A? = A()
a = nil
Output:
// Init of A
// Init of B
// Some Property of A
// 20
// Deinit of A
// Deinit of B
Now the second scenario which will cause the reference cycle.
/******** Scenario 2 **********/
class A {
var b:B?
let p = "Some Property of A"
init() {
print("Init of A")
self.b = B(closure: { (number) -> Void in
print(self.p) // capturing self but NEED to write [weak/unowned self]
print(number)
})
}
deinit {
print("Deinit of A")
}
}
// Suppose this is a library class whose implementation is hidden
class B {
let closure:(Int->Void)
init(closure:(Int->Void)) {
print("Init of B")
self.closure = closure //class B owns the closure here
f()
}
func f() {
self.closure(20)
}
deinit {
print("Deinit of B")
}
}
var a:A? = A()
a = nil
The idea of "owning" is probably the wrong terminology here. Objective-C & Swift use ARC to manage memory. It's a system of references of varying types (strong, weak, and unowned). And it's important to note that unless a reference is marked as being weak or unowned, it's strong.
So, let's start by taking a look at your first example and pay attention to your references.
Below your class declarations, we have this code:
var a:A? = A()
a = nil
Because a isn't marked as weak or unowned, it's a strong reference to the object that we also create in the same line that we declare a. So, a is a strong reference to this object until a no longer points to that object (which in this case happens in the second line). And as we know, strong references prevent deallocation from happening.
Now let's dive into A's init method, which we're actually calling in this line.
init() {
print("Init of A")
self.b = B(closure: { (number) -> Void in
print(self.p)
print(number)
})
}
The first thing A's init does is print "Init of A", and that's the first thing we see looking at what is printed.
The next thing it does is assign a value to it's b property. It's b property also isn't marked as weak or unowned, so this is a strong reference.
And the value it is assigning to b is a newly constructed instance of the B class, which explains the second line we see printed: "Init of B", as that's the first line of B's initializer.
But B's initializer takes a closure. Here's the closure we've passed it:
{ (number) -> Void in
print(self.p) // capturing self but still no need to write [weak/unowned self]
print(number)
}
This block absolutely does hold a strong reference to self (in this case, the instance of a which printed "Init of A" earlier.
So, why isn't there a retain cycle? Well, let's look at B's initializer. What does it do with the closure?
class B {
init(closure:(Int->Void)) {
print("Init of B")
// ... do some work here
closure(20)
}
deinit {
print("Deinit of B")
}
}
So, when we instantiate an instance of B, it fires the closure, then forgets about it. No strong reference is ever made to the closure which we passed in.
So, let's take a look at our references:
global --strong--> a
closure --strong--> a
a --strong--> b
So, b will continue to have a strong reference and continue to exist for as long as a exists and maintains its strong reference to it. And a will continue to exist as long as at least one thing between your global reference and the closure continue to exist and maintain their strong reference to it.
But notice, nothing is keeping a strong reference to the closure. At least, not beyond the scope of whatever method it is used in.
The B initializer maintains a strong reference to the closure passed into it only until the end of the initializer.
So, when we write this line:
var a:A? = A()
By the time A() has returned, the variable a remains the only strong reference to a, and a remains the only strong reference to b. The closure, which had the potential to create a reference cycle no longer exists. Then, when we set a to be nil, we kill our strong reference to a.
a = nil
So, a deallocates. Upon that happening, there remain no strong references to b, so it also deallocates.
Your second example is different. In the second example, the implementation of A remains the same, but the implementation of B has changed. B now has this closure property which keeps a strong reference to whatever closure is passed into B's initializer.
So now, our references look like this:
global --strong--> a
closure --strong--> a
a --strong--> b
b --strong--> closure
So you can see, even if we break the global reference to a, there still exists a retain cycle:
a --> b --> closure --> a
If we do not make use of [weak self] or [unowned self], the closure absolutely has a strong reference to self. Whether or not that creates a retain cycle depends on what has strong references to the closure.
In order to determine that for 3rd party libraries, start by checking the source code or documentation. Outside of Apple, I don't currently know how to distribute a Swift library with private implementation that we can't investigate, and Apple's code is all well documented. But assuming the worst, assuming we really have no means, then treat any closure you pass to the third party library as something that the library will hold a strong reference to.
Even this doesn't necessarily mean we must always use [weak self] or [unowned self] in our closures.
If we notice in the example above, there are multiple ways to break the retain cycle. Remember what it looks like:
a -> b -> closure -> a
So, using [weak self] or [unowned self] would prevent the retain cycle as it would eliminate the closure's strong reference to a. But even if the closure maintains a strong reference to a, notice that if we break a's strong reference to b that the cycle breaks. Nothing holds a strong reference b so b will deallocate. That will leave nothing holding a strong reference to the closure, allowing the closure to deallocate, and then, nothing (at least within this cycle) is keeping a alive.
So if we pass a closure that does something like this...
{ (number) in
print(self.p)
print(number)
self.b = nil
}
That third line just so happens to break the cycle because now self no longer holds a strong reference to the b which holds the strong reference to the closure which holds the strong reference back to self.