Skip to main content

Swift Weak and Unowned References

Tags

Weak and Unowned

I see a lot of discussion on the internet about the usage of weak, and unowned objects in Swift.  Recently, I was involved in a thread on Stack Overflow discussing the proper usage of weak references and it occurred to me that since I see so much discussion about this topic it would be good to write a post detailing my understanding on the usage of weak references.  So, that is what I have done here, I put together a small blog post on what I understand to be the difference between weak and unowned references in Swift 3 and how to work with them properly when dealing with closures.  Let's get started!

Probably the first thing to define is what the terms closure, weak, and unowned mean in terms of Swift.  To do that I thought I would give a formal definition based upon my understanding, and then provide some actual code examples to help elaborate things a bit more.  

First, what is a closure?  A closure is similar to a block back in Objective-C, but more formally in Swift it is a self contained bit of functionality that can be assigned to variables and then passed around in your program to be executed when needed.  This is why Swift closures show up as completion operations because the desired completion behavior can be defined as a variable and then passed in to a method to actually perform the completion behavior.  Allowing the developer to override or define any custom behavior desired when working with iOS API's or 3rd ones.

Second, what is a weak reference?  Well, to explain what a weak reference is, I should probably explain what a strong reference is also.  A strong reference to an object is a reference that you control as a developer.  When you create a strong reference to an object, you are telling the compiler that you will be in charge of that object's life cycle and you know when is best to free the object's memory.  Creating a strong reference to an object will keep that object in memory as long as a strong reference to that object is still preserved.  This strong reference will even be preserved if the application receives a low memory warning and objects are freed up to enhance performance.

So, now that I have defined what a strong reference is, what is a weak reference?  A weak reference is a reference that does not retain the instance that it is referring too.  For example, a weak reference does not keep a strong hold on a particular instance like a strong reference does.  So how does a weak reference work then if it does not keep a strong hold on an instance?  Weak references work because they weakly reference a strongly retained instance.  If a strongly retained instance is cleaned up then the weak reference is either left dangling or it will be set to nil once the compiler cleans it up.  Also, under memory pressure, the compiler will clean up any weak references because they are low hanging fruit and should not be expected to produce undefined behavior if your are guard all of your weak references properly.

Lastly, what is an unowned reference?  Well, unowned is a unique reference because unowned is a Swifty thing that was introduced as a way for an object to be completely present or completed removed.  That way there is no option for a reference to be set to nil and thus you do not have to unwrap it like you do a weak reference.  Think of unowned references much like weak references except unowned references are not optional because they cannot be set to nil and they are completely removed from your program when you receive low memory warning because the compiler has cleaned them up.

Closure

Closures

The standard example of a closure is a completion handler that executes a desired set of functionality after the completion of a method.  To extra illustrate this type of closure I thought I would provide an example with a custom defined closure that is assigned to a variable to be executed at the end of URLSession's dataTask to override the existing completion handler provided by the API.

In this example below, networkArticlesCompletionHander is defined a closure that takes the types Data, URLResponse, and Error passed into it when the closure is executed and methods and data that is attached to self can be executed in the context of this closure when the logic is parsed.

public func executeNetworkDataTask() {
 
    // Declare a completion closure as a variable and then pass it to the Network object to perform the completion task
    let networkArticlesCompletionHander: (Data?, URLResponse?, Error?) -> Void = {
        [unowned self] (data, response, error) in
 
        guard error == nil else {
            // Display error alert with usage of unowned self that an error is present
            DispatchQueue.main.async {
                self.displayError(message: error.debugDescription)
            }
            return
        }
 
        guard let networkData = data, networkData.count > 0 else {
            // Display error alert with usage of unowned self that data was not returned
            DispatchQueue.main.async {
                self.displayError(message: "There was an error parsing network data.")
            }
            return
        }
 
        do {
            if let networkArticles = try JSONSerialization.jsonObject(with: networkData, options: []) as? [[String: AnyObject]] {
                var collectedArticles: [Article] = []
 
                // Create article objects out of a JSON object
                for networkArticle in networkArticles {
                    let article = Article(jsonObject: networkArticle)
                    collectedArticles.append(article)
                }
                DispatchQueue.main.async {
                    // Usage of unowned self
                    self.articles = collectedArticles
                    self.activityView.alpha = 0.0
                    self.tableView.reloadData()
                }
            }
        } catch let error as NSError {
            DispatchQueue.main.async {
                // Usage of unowned self
                self.displayError(message: error.debugDescription)
            }
        }
    }
 
    // Load over the network using custom defined closure as a completion block that makes use of unowned self
    loadNewArticlesWithAssignedCompletion(networkArticlesCompletionHander: networkArticlesCompletionHander)
 
}
 
public func loadNewArticlesWithAssignedCompletion(networkArticlesCompletionHander: @escaping(Data?, URLResponse?, Error?) -> Void) {
 
    guard let articleURL = URL(string: url) else {
        return
    }
 
    var articleRequest = URLRequest(url: articleURL)
    articleRequest.httpMethod = "GET"
 
    let urlSession = URLSession(configuration: urlConfiguration)
 
    let articleTask = urlSession.dataTask(with: articleRequest, completionHandler: networkArticlesCompletionHander)
    articleTask.resume()
}
Weak Self

Weak

In the code example that I provided below I have a Network instance that is being used to process and delegate the flow of network activity between business objects and the ViewController.  In this example the application is asking for some data and then leaving the responsibility up to the to the Network instance to notify the ViewContoller when the network request has fully loaded and there is data ready to be used in the ViewContoller.  To achieve this URLSession's dataTask is being utilized again, but instead of passing in a custom completion handler, the Network Instance is using the default, out of the box, one instead.  Once the dataTask completes, the completion handler executes and an action is taken utilizing the NetworkProtocol to notify the ViewController of a success or a failure.  

NetworkProtocol in this example is a weak instance property of the Network instance.  As you may have noticed, the NetworkProtocol object is needed inside the completion handler to notify the ViewController to take action based upon the result of the dataTask.  To avoid the potential for a strong retain cycle from the Network instance to the NetworkProtocol, I pass in a reference to weak self and now when I use self inside the closure I am weakly referencing the strong instance property of Network that in-turn points at the weak reference of NetworkProtocol.  As to avoid a strong instance pointing at a strong instance.

Also notice that in this example self has a question mark next to it; this is because the weak reference to self is an optional value and can be set to nil at any time.  Keep in mind that the weak reference to self could be set to nil if the strong reference to Network is deallocated or the application receives a memory warning.

class Network: NSObject {
 
    //
    // MARK: - Shared instance property
    //
    public static var shared = Network()
 
    //
    // MARK: - Public instance properties
    //
    public weak var networkDelegate: NetworkProtocol?
 
    //
    // MARK: - Private constants
    //
    private let url: String = "https://5fc3d7589074cd0c4bf5-79ef711e857aec8d77eb74e0027f6262.ssl.cf1.rackcdn.com/articles.json"
    private let urlConfiguration = URLSessionConfiguration.default
 
    //
    // MARK: - Public Instance Methods
    //
 
    // Load articles over the network with a default completion block provided by URLSession
    public func loadNewArticlesWithDefaultCompletion() {
 
        guard let articleURL = URL(string: url) else {
            return
        }
 
        var articleRequest = URLRequest(url: articleURL)
        articleRequest.httpMethod = "GET"
 
        let urlSession = URLSession(configuration: urlConfiguration)
 
        // Create a dataTask with a closure that defines the comletion handler
        // The closure in this case is defined as completionHandler: { (data, response, error) in  ... }
        let articleTask = urlSession.dataTask(with: articleRequest, completionHandler: { [weak self] (data, response, error) in
 
            // Ensure that an error is not present, otherwise, return the error
            guard error == nil else {
                // Usage of weak self to reference networkDelegate
                DispatchQueue.main.async {
                    self?.networkDelegate?.networkReceivedError(error: error.debugDescription)
                }
                return
            }
 
            // Ensure that the network data is available and that the byte count is greater that zero
            guard let networkData = data, networkData.count > 0 else {
                DispatchQueue.main.async {
                    self?.networkDelegate?.networkReceivedError(error: "There was an error parsing network data.")
                }
                return
            }
 
            // Perform the JSONSerialization into an array of Dictionaries
            // Next use the convenience constructor in Article to create an Article object out of each object in the JSON array
            do {
                if let networkArticles = try JSONSerialization.jsonObject(with: networkData, options: []) as? [[String: AnyObject]] {
                    var articles: [Article] = []
 
                    // Create article objects out of a JSON object
                    for networkArticle in networkArticles {
                        let article = Article(jsonObject: networkArticle)
                        articles.append(article)
                    }
                    DispatchQueue.main.async {
                        self?.networkDelegate?.articlesFinishedLoading(articles: articles)
                    }
                }
            } catch let error as NSError {
                DispatchQueue.main.async {
                    self?.networkDelegate?.networkReceivedError(error: error.debugDescription)
                }
            }
 
        })
        articleTask.resume()
    }
}
Unowned Self

Unowned

To demonstrate an example of unowned I thought I would go back to my example of the closure.  In this example I am creating a custom completion handler like before and because of the lexical context of which I am instructing the completion handler to execute, I am able to take advantage of instance methods available to the completion handler at the time in which the closure was defined.  For example, in this example you will notice that I am referencing instance method displayError and the instance property of articles.  This is because I am able to define a reference to self that points to this method and property to assign execution when the completion handler runs.  To do this though,  I cannot create a strong retain cycle to the instance property of self that I am using.  Instead, much like weak, I can take advantage of unowned to have a non-optional reference to self that is present for as long as the outer instance property of self is present and is not deallocated.  When the instance level value of self is deallocated, the unowned value is not set to nil, but instead, is removed from the program and does not exist anymore.

public func executeNetworkDataTask() {
    // Declare a completion closure as a variable and then pass it to the Network object to perform the completion task
    let networkArticlesCompletionHander: (Data?, URLResponse?, Error?) -> Void = {
        [unowned self] (data, response, error) in
 
        guard error == nil else {
            // Display error alert with usage of unowned self that an error is present
            DispatchQueue.main.async {
                self.displayError(message: error.debugDescription)
            }
            return
        }
 
        guard let networkData = data, networkData.count > 0 else {
            // Display error alert with usage of unowned self that data was not returned
            DispatchQueue.main.async {
                self.displayError(message: "There was an error parsing network data.")
            }
            return
        }
 
        do {
            if let networkArticles = try JSONSerialization.jsonObject(with: networkData, options: []) as? [[String: AnyObject]] {
                var collectedArticles: [Article] = []
 
                // Create article objects out of a JSON object
                for networkArticle in networkArticles {
                    let article = Article(jsonObject: networkArticle)
                    collectedArticles.append(article)
                }
                DispatchQueue.main.async {
                    // Usage of unowned self
                    self.articles = collectedArticles
                    self.activityView.alpha = 0.0
                    self.tableView.reloadData()
                }
            }
        } catch let error as NSError {
            DispatchQueue.main.async {
                // Usage of unowned self
                self.displayError(message: error.debugDescription)
            }
        }
    }
 
    // Load over the network using custom defined closure as a completion block that makes use of unowned self
    Network.shared.loadNewArticlesWithAssignedCompletion(networkArticlesCompletionHander: networkArticlesCompletionHander)
}
 
 
public func displayError(message: String) {
    let alertViewController = UIAlertController(title: "Network Error",
                                                message: message,
                                                preferredStyle: .alert)
 
    let dismiss = UIAlertAction(title: "OK",
                                style: .default,
                                handler: nil)
    alertViewController.addAction(dismiss)
 
    present(alertViewController, animated: true)
}

Well, that is it for my explanation of Weak and Unowned references in Swift.  Please, if you have questions or see a part of my article that needs to be corrected, leave a comment and let me know.  I will try and address all comments as soon as possible.

The complete application that was used in this article is available for download on my Github here.  Please feel free to fork the code or provide helpful edits as you see fit.  Thank you for reading!

Member for

3 years 9 months
Matt Eaton

Long time mobile team lead with a love for network engineering, security, IoT, oss, writing, wireless, and mobile.  Avid runner and determined health nut living in the greater Chicagoland area.