Laurence Wingo
Laurence Wingo

Reputation: 3952

Understanding why using weak deallocates stored properties after being instantiated inside a function

Even after reading Swift's documentation about ARC, I'm still having trouble understanding why a property was set to nil even when it's instantiated within a function. I have the following sample code:

import UIKit

class ItemListViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView = UITableView()

    }


}

And here is the sample code for the test run:

import XCTest
@testable import PassionProject

class ItemListViewControllerTests: XCTestCase {

    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }
    func tests_TableViewIsNotNil_AfterViewDidLoad(){

        let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: "ItemListViewController")
        let sut = viewController as! ItemListViewController
        _ = sut.view
        XCTAssertNotNil(sut.tableView)
    }

    func tests_loadingView_SetsUITableViewDataSource(){

    }

}

Inside the function that is being tested, we've created the correct instances and I understand that calling the view property fires the viewDidLoad() method. I'm not sure why the test states that the tableView property is nil when running the test. I'd like to understand ARC from this perspective and realize what is happening under the hood when the test states that the tableView property is nil. We've clearly instantiated the UITableView object inside viewDidLoad(). I understand that this test passes when removing "weak" from the tableView property but I don't understand why. Any explanation would be greatly appreciated to understand this better.

Upvotes: 2

Views: 155

Answers (1)

matt
matt

Reputation: 535401

I'm still having trouble understanding why a property was set to nil even when it's instantiated within a function

@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    tableView = UITableView()
}

Then you haven't understood what weak means. It means: this reference has no ability to keep this object alive. Thus, if nothing else keeps this object alive (i.e. retains it), the object will die no matter how it is instantiated.

Your code creates the table view: UITableView(). It assigns the table view to a variable: tableView = UITableView(). But that variable is weak. It has no power to keep this table view alive. And you didn't do anything else that would keep this table view alive. Therefore the table view instantly dies, vanishing in a puff of smoke, and leaving tableView pointing at nothing — and so tableView is reset to nil.

The usual thing, however, is to talk like this:

super.viewDidLoad()
let tv = UITableView(frame:someRect)
self.view.addSubview(tv)
tableView = tv

That makes all the difference in the world. The tableView variable is still weak, and still can't keep the table view alive. But we added the table view as a subview to self.view, and now its superview keeps it alive.

And that is the reason why outlets are often weak. It's because they refer to views that have superviews that already keep them alive, so there is no need for a second reference (the outlet property) to keep them alive as well.

Upvotes: 8

Related Questions