Delegate Pattern and Strong Reference Cycle In Swift


swift

Delegate Pattern and Strong Reference Cycle In Swift


Daniel Shin - August 24, 2015

This post is for self-documenting purpose but if anyone finds it helpful, I’ll be glad.

Memory management is built into the language itself in Swift, called ARC (Automatic Reference Counting). Refer to this and this.

For most mundane works, ARC works fine and you may safely strong reference your objects. However, as your code becomes more adventurous and intertwined (not necessarily due to the failure in design but rather the sheer complexity of the program), your program will have a few or more strong reference cycle.

Swift provides weak and unowned to let you avoid these cases as explained in this post.

Delegate Pattern

Delegate pattern is a simple and yet very powerful design pattern in iOS/OSX. It lets the delegating object to hold a reference to delegate in order to pass message in the future at its will.

This pattern is widely used in UIKit where the framework object acts as a delegating object and your ViewController acts as a delegate by implementing the protocol stipulated by the delegating object.

Let’s look at an example.

UITableViewController is the delegating object and UITableViewControllerDelegate is the protocol defined by delegating object that must be implemented by wanna-be delegate.

MyTableViewController is my custom object that subclasses UITableViewController, and can also choose to implement UITableViewControllerDelegate to act upon delegate methods. Most prominent use for delegate pattern is to update UI (the responsibility of delegate) in response to events sent by delegating object (in the form of simply calling the defined delegate methods).

Most people, and rightly so, choose to implement UITableViewControllerDelegate within the custom UITableViewController itself, which is, in this case, MyTableViewController. But remember that MyTableViewController is a subclass of UITableViewController? Now it’s also a delegate of UITableViewController.

It’s a delegate and subclass of UITableViewController. If you think about it, this is clearly a Strong Reference Cycle since MyTableViewController subclasses UITableViewController, which means delegate to UITableViewController is, by extension, a delegate to MyTableViewController, which is itself.

But don’t worry. This is how Apple intended us to use it by keeping a weak reference to delegate in UITableViewController’s implementation. Most, if not all, delegates in UIKit are weakly referenced. This is to avoid Strong Reference Cycle caused by requiring one object to be both subclass and delegate.

Big win for Apple here.

When you design this delegate pattern yourself, be like Apple and make sure to mark delegate as weak if you intend that class to be both subclassed and delegated by the same object, which is a commonplace.


This rule applies equally when your custom object holds a strong reference to delegating object and your custom object is, in turn, the delegate of that retained delegating object.

For example, think of this imaginary AudioPlayer class. In MyTableViewController, I want to play an audio so I instantiated by self.audioPlayer = AudioPlayer(). AudioPlayer also provides some delegate methods to notify client when the audio has reached the end.

protocol AudioPlayerDelegate {
  func audioPlayerDidReachEnd()
}

When the audio stops, I need to update UI so I decided to make my MyTableViewController as a delegate of AudioPlayer by implementing AudioPlayerProtocol and passing itself by audioPlayer.delegate = self.

This is, again, a Strong Reference Cycle because MyTableViewController strongly references self.audioPlayer, while self.audioPlayer, again, strongly references self, which is MyTableViewController, the delegate of AudioPlayer.

This type of cycle can be just as easily broken by one of the two ways.

One is to mark self.audioPlayer as weak.

class MyTableViewController {
  private weak var audioPlayer:AudioPlayer!
  override viewDidLoad() {
    self.audioPlayer = AudioPlayer()
  }
}

Or if AudioPlayer’s implementation is available, simply mark the delegate as weak.

class AudioPlayer {
  private weak var delegate:AudioPlayerDelegate!
  init(delegate:AudioPlayerDelegate) {
    self.delegate = delegate
  }
}

First approach is unsound both semantically and architecturally Semantically, because self.audioPlayer really should never be nil as long as MyTableViewController is alive and architecturally, because class (or, more properly server) fails to encapsulate its implementation from the client.

Second approach is the way to go. Semantically, delegating object is less concerned with the existence of delegate than the other way around. Delegating object must be retained to properly notify delegate as long as the delegate is alive while delegate object may be released even though delegating object is still alive. In such case, delegating object will simply stop sending messages.


Addendum

You may just as well use unowned in place of weak if the delegate is set within init() method.

weak can only be used with var since the nature of a reference being weak means that the refereneced object may at anytime be released.

By the same token, unowned must be used with let only. It implicitly asserts that the referenced object must be alive throughout its user’s lifetime.

Use unowned let whenever you set the reference in init() method and weak var for everything else.