2

How do I prevent a retain cycle when passing around functions as objects in Swift

Imagine you have a datasource object like this

import UIKit
class MagicDataSource:NSObject,UITableViewDatasource {

    deinit {
        println("bye mds")
    }

    //cant use unowned or weak here
    var decorator:((cell:CustomCell)->Void)?

    func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)->UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier(Identifier, forIndexPath: indexPath) as CustomCell

        decorator?(cell)
        return cell
    }

}

And a view controller like this which has (and wants) a strong ref to that object

import UIKit
class ViewController: UIViewController {

    var datasource:MagicDataSource? = MagicDataSource()

    deinit {
        println("bye ViewCon")
    }

    override func viewDidLoad() {

        super.viewDidLoad()
        datasource?.decorator = decorateThatThing
    }

    func decorateThatThing(cell:CustomCell) {

        //neither of these two are valid             
        //[unowned self] (cell:CustomCell) in
        //[weak self] (cell:CustomCell) in

        cell.theLabel.text = "woot"

    }
}

When you discard the view controller , the datasource will not be released and neither will the view controller as it holds a strong ref to the decorateThatThing function on the view controller.

You can stop the cycle and get the decorator to release by doing this in ViewController but it feels messy

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
     datasource?.decorator = nil
}

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    datasource?.decorator = decorateThatThing
}

so the question is how do i declare vars and/or functions to avoid having to teardown the datasource manually so that when the view controller is discarded the associated datasource is released too.

Warren Burton
  • 17,451
  • 3
  • 53
  • 73

1 Answers1

3

Rather than

datasource.decorator = decorateThatThing

You can use

datasource.decorator = { [unowned self] cell in
    self.decorateThatThing(cell)
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • FYI using `unowned` will cause a crash if `self` was deallocated when `decorator` is invoked. You can also use `weak` in the [capture list](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID57). – Brian Gerstle Jul 07 '15 at 17:22
  • Yep, if there is _any_ chance that `self` could be deallocated while `decorator` is invoked, then definitely use `weak`. But if that is not possible, then use `unowned`. Using `weak` isn't expensive, but it also isn't free. – Rob Jul 07 '15 at 18:25