Function references in Swift and retain cycles

09 Oct, 2014
Xebia Background Header Wave

The Swift programming language comes with some nice features. One of those features are closures, which are similar to blocks in objective-c. As mentioned in the Apple guides, functions are special types of closures and they too can be passed around to other functions and set as property values. In this post I will go through some sample uses and especially explain the dangers of retain cycles that you can quickly run into when retaining function pointers.

Let's first have a look at a fairly simple objective-c sample before we write something similar in Swift.


We will create a button that executes a block statement when tapped. In the header file we define a property for the block: [objc] @interface BlockButton : UIButton @property (nonatomic, strong) void (^action)(); @end [/objc] Keep in mind that this is a strong reference and the block and references in the block will be retained. And then the implementation will execute the block when tapped: [objc]

import "BlockButton.h"

@implementation BlockButton -(void)setAction:(void (^)())action { _action = action; [self addTarget:self action:@selector(performAction) forControlEvents:UIControlEventTouchUpInside]; } -(void)performAction { self.action(); } @end [/objc] We can now use this button in one of our view controllers as following: [objc] self.button.action = ^{ NSLog(@"Button Tapped"); }; [/objc] We will now see the message "Button Tapped" logged to the console each time we tap the button. And since we don't reference self within our block, we won't get into trouble with retain cycles. In many cases however it's likely that you will reference self because you might want to call a function that you also need to call from other places. Let's look as such an example: [objc] -(void)viewDidLoad { self.button.action = ^{ [self buttonTapped]; }; } -(void)buttonTapped { NSLog(@"Button Tapped"); } [/objc] Because our view controller (or it's view) retains our button, and the button retains the block, we're creating a retain cycle here because the block will create a strong reference to self. That means that our view controller will never be deallocated and we'll have a memory leak. This can easily be solved by using a weak reference to self: [objc] __weak typeof(self) weakSelf = self; self.button.action = ^{ [weakSelf buttonTapped]; }; [/objc] Nothing new so far, so let's continue with creating something similar in Swift.


In Swift we can create a similar Button that executes a closure instead of a block: [objc] class ClosureButton: UIButton { var action: (() -> ())? { didSet { addTarget(self, action: "callClosure", forControlEvents: .TouchUpInside) } } func callClosure() { if let action = action { action() } } } [/objc] It doing the same as the objective-c version (and in fact you could use it from objective-c with the same block as before). We can assign it an action from our view controller as following: [objc] button.action = { println("Button Tapped") } [/objc] Since this closure doesn't capture self, we won't be running into problems with retain cycles here. As mentioned earlier, functions are just a special type of closures. Which is pretty nice, because it lets us reference functions immediately like this: [objc] override func viewDidLoad() { button.action = buttonTapped } func buttonTapped() { println("Button Tapped") } [/objc] Nice and easy syntax and good for functional programming. If only it wouldn't give us problems. Without it being immediately obvious, the above sample does create a retain cycle. Why? We're not referencing self anywhere? Or are we? The problem is that the buttonTapped function is part of our view controller instance. So when the button.action references to that function, it creates a strong reference to the view controller as well. In this case we could fix it by making buttonTapped a class function. But since in most cases you'll want to do something with self in such a function, for example accessing variables, this is not an option. The only thing we can do to fix this is to make sure that the button won't get a strong reference to the view controller. Just like in our last objective-c sample, we need to create a weak reference to self. Unfortunately there is no easy way to simply get a weak reference to our function. So we need a work around here.

Work around 1: wrapping in closure

We can create a weak reference by wrapping the function in a closure: [objc] button.action = { [weak self] in self!.buttonTapped() } [/objc] Here we first create a weak reference of self. And in Swift, weak references are always optional. That means self within this closure is now an optional and need to unwrap it first, which is what the exclamation mark is for. Since we know this code cannot be called when self is deallocated we can safely use the ! instead of ?. A lot less elegant than immediately referencing our function immediately. In theory, using an unowned reference to self should also work as following: [objc] button.action = { [unowned self] in self.buttonTapped() } [/objc] Unfortunately (for reasons unknown to me) this crashes with a EXC_BAD_ACCESS upon deallocation of the ClosureButton. Probably a bug.

Work around 2: method pointer function

Thanks to a question on StackOverflow about this same problem and an answer provided by Rob Napier, there is a way to make the code a bit more elegant again. We can define a function that does the wrapping in a closure for us: [objc] func methodPointer<T: AnyObject>(obj: T, method: (T) -> () -> Void) -> (() -> Void) { return { [weak obj] in method(obj!)() } } [/objc] Now we can get a weak reference to our function a bit easier. [objc] button.action = methodPointer(self, ViewController.buttonTapped) [/objc] The reason this works is because you can get a reference to any instance function by calling it as a class function with the instance (in this case self) as argument. For example, the following all does the same thing: [objc] // normal call self.buttonTapped() // get reference through class let myFunction = MyViewController.buttonTapped(self) myFunction() // directly through class MyViewController.buttonTapped(self)() [/objc] However, the downside of this is that it only works with functions that take no arguments and return Void. i.e. methods with a () -> () signature, like our buttonTapped. For each signature we would have to create a separate function. For example for a function that takes a String parameter and returns an Int: [objc] func methodPointer<T: AnyObject>(obj: T, method: (T) -> (String) -> Int) -> ((String) -> Int) { return { [weak obj] string in method(obj!)(string) } } [/objc] We can then use it the same way: [objc] func someFunction() { let myFunction = methodPointer(self, MyViewController.stringToInt) let myInt = myFunction("123") } func stringToInt(string: String) -> Int { return string.toInt() } [/objc]

Retain cycles within a single class instance

Retain cycles do not only happen when strong references are made between two instances of a class. It's also possible, and probably less obvious, to create a strong reference within the same instance. Let look an an example: [objc] var print: ((String) -> ())? override func viewDidLoad() { print = printToConsole } func printToConsole(message: String) { println(message) } [/objc] Here we do pretty much the same as in our button examples. We define an optional closure variable and then assign a function reference to it. This creates a strong reference from the print variable to self and thus creating a retain cycle. We need to solve it by using the same tricks we used earlier. Another example is when we define a lazy variable. Since lazy variables are assigned after initialisation, they are allowed to reference self directly. That means we can set them to a function reference as following: [objc] lazy var print: ((String) -> ()) = self.printToConsole [/objc] Of course this also creates a retain cycle.


To avoid creating retain cycles in Swift you should always remember that a reference to an instance function means that you're referencing the instance as well. And thus when assigning to a variable, you're creating a strong reference. Always make sure to wrap such references in a closure with a weak reference to the instance or make sure to manually set the variables to nil once you're done with them. Unfortunately Swift does not support weak closure variables, which is something that would solve the problem. Hopefully they will support it in the future or come up with a way to create a weak reference to a function much like we can use [weak self] now in closures.

Explore related posts