Swift Memory management when pass a closure as parameter to singleton

2.2k views Asked by At

I'm aware of the fact that closure can create retain cycles if it is assigned to a property of a class and instance properties of the class are used inside of the closure. But

1) what about the closure is not assigned to the class property but passed as parameter to a singleton's class method ?

2) How the memory is managed in this case ?

In a method of my controller (UIViewController) I have something like:

MySingleton.classMethod(parameters ..., completion: { () -> Void in
   /**
   doing stuff here
   */
})
1

There are 1 answers

7
Georgi Boyadzhiev On

If You are not assigning the closure to a property, but just passing it to a function You need to consider if the closure will be escaping or noescape. In Swift 3 by default all closures passed to functions are non-escaping.

Here is a little bit more info on the matter:

"noescape" - the passed closure is invoked before the return of the function

"escaping" - If a closure is passed as an argument to a function and it is invoked after the function returns, the closure is escaping.

Simple explanation : We need to mark closure as escaping when we pass it to a function and the closure will be called after this function returns.

General rule is when the closure is escaping You need to use capture list to prevent retain cycles.

Example

let closure = { [weak someVariable] (name: Type) -> Void in
....
// code
}

Additional info:

One weird thing is that optional closures are treated as escaping. And when we explicitly add escaping keyword, there is a compile error -escaping attribute only applies to function types.

See the below links for more info.

https://bugs.swift.org/browse/SR-2053

https://stackoverflow.com/a/39619298/5388473

https://stackoverflow.com/a/39846519/5388473

Update
The idea of using weak or unowned in the capture lists of escaping closures is that essentially We don't know what will happen with the passed escaping closure, it may be called sometime later from another function or it may be stored in some object, which may lead to a strong retain cycle.

weak vs unowned vs strong capture

For more detailed information check Apple ARC docs.

From Apple docs:

Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references.

Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle.

Use a weak reference when the other instance has a shorter lifetime—that is, when the other instance can be deallocated first. Use an unowned reference when the other instance has the same lifetime or a longer lifetime.

Keep in mind that the weak approach will add a boiler plate to the code and will be a little bit more slow, because ARC will add code for setting the weak variables to nil on their deallocation. It is a good practice to follow the rules stated above, when choosing weak or unowned.