Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Block retain cycles in Swift?

Tags:

swift

Traditionally in Objc, we do weakSelf to prevent additional retain count for blocks.

How does swift internally manage retain cycles that occur in blocks for Objc?

like image 754
Adrian Avatar asked Jun 04 '14 16:06

Adrian


People also ask

How do I stop retaining cycles in Swift?

For example; when we set mercedes instance to nil , the compiler cannot decrease its ARC to 1 because a Car object is still referencing the mercedes and vice versa. In order to prevent this retain cycle, we need to declare at least one of the variable as weak or unowned.

How would you avoid retain cycles when using closures blocks in Swift?

We can solve this in two ways. First, we can use [unowned self]: Now the closure doesn't have a strong reference anymore. Just be careful when using [unowned self] since that, if the object has already been deallocated when the closure is called, a crash will occur.

What are retain cycles?

What are retain cycles and memory leaks? A memory leak in iOS is when an amount of allocated space in memory cannot be deallocated due to retain cycles. Since Swift uses Automatic Reference Counting (ARC), a retain cycle occurs when two or more objects hold strong references to each other.


2 Answers

To prevent a block from holding a strong reference to an object, you must define a capture list for the block.

The closure expression syntax is defined as follows:

{ ( /*parameters*/ ) -> /*return type*/ in      // statements } 

But this is extended later in the documentation to include a capture list. This effectively equates to the expression syntax being defined as follows:

{ [ /*reference type*/ /*object*/, ... ] ( /*parameters*/ ) -> /*return type*/ in      // statements } 

...where /*reference type*/ can be either weak or unowned.

The capture list is the first thing to appear in the closure and it is optional. The syntax, as shown above is defined as one or more pairs of reference type followed by object; each pair is separated by a comma. For example:

[unowned self, weak otherObject] 

Complete example:

var myClosure = {     [unowned self] in     print(self.description) } 

Note that an unowned reference is non-optional, so you don't need to unwrap it.

Hopefully that answers your question. You can read up more about ARC in Swift in the relevant section of the documentation.

You should pay particular attention to the difference between weak and unowned. It could be safer in your implementation to use weak, because using unowned assumes the object will never be nil. This may lead to your app crashing if the object has actually been deallocated before being used in your closure.

Using weak as the reference type, you should unwrap with ?, as follows:

var myClosure = {     [weak self] in     print(self?.description) } 
like image 68
Sam Avatar answered Sep 24 '22 14:09

Sam


The only thing that threw me off with capture lists was when to use weak vs unowned.

The book distilled it down to these rules:

If self could be nil in the closure use [weak self].

If self will never be nil in the closure use [unowned self].

See the section Weak and Unowned References in The Swift Programming Language book for a deeper explanation.

like image 35
TenaciousJay Avatar answered Sep 22 '22 14:09

TenaciousJay