1

I understand the typical retain cycle with a closure when referencing self within the closure, but is there a reason the label does not produce a retain cycle? I've tested commenting the testClosure out vs commenting the label out and only the testClosure produced a retain cycle

class TargetView: UIView {

    let newText = "hi"
    var label = UILabel()

    private var counter = 0
    private var testClosure : (() -> ()) = { }

    init() {
        super.init(frame: .zero)
        // This causes a retain cycle
        testClosure = {
            self.counter += 1
            print(self.counter)
        }

        // This does NOT cause a retain cycle
        label = UILabel() {
            $0.text = self.newText
            self.counter += 1
        } 
    }

    required init?(coder aDecoder: NSCoder) { fatalError() }

    deinit {
        print("view deinit!")
    }
}

extension UILabel {
    convenience init(label: (UILabel) -> Void) {
        self.init()
        label(self)
    }
}
jo1717a
  • 11
  • 1
  • 3
    There's nothing holding on to the block in the second case, it goes away after the initializer finishes – dan Jul 13 '18 at 21:24
  • 1
    In the first case self is captured as strong when testClosure is initialized, but the second case you are just passing a closure as a parameter that has to be executed after UILabel initialization – Ronald Jul 13 '18 at 23:13

1 Answers1

0

As already described in the comments: your custom initializer for UILabel just runs the label block but does not store (read: does not retain) it, so no retain cycle can occur.

Please not that from the outside you cannot determine whether a block parameter is retained, so unless this behaviour is documented or you wrote the code yourself, it's often better to use a weak reference just to be sure.

dr_barto
  • 5,723
  • 3
  • 26
  • 47