Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Property Initialization with closures

I was looking into ARC and strong reference cycles and ran into this code of mine:

class TestClass: UIView {
  let button: UIButton = {
    let view = UIButton()
    view.frame = CGRect(x: 50, y: 50, width: 200, height: 200)
    view.backgroundColor = .blue
    view.translatesAutoresizingMaskIntoConstraints = false
    view.setTitle("Button", for: .normal)
    view.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
    return view
  }()

  @objc private func buttonClicked() {
    print("Clicked")
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
    print("Object of TestClass initialized")
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  deinit {
    print("Object of TestClass deinitialized")
  }
}

reference to self in the addTarget method inside the closure doesn't seem to create a strong reference cycle.

Can someone explain why?

Also, I noticed that if I remove inheritance from UIView the compiler starts complaining: Use of unresolved identifier 'self'.

Can someone explain this as well, why does it happen in this case and doesn't in the first one?

like image 899
Alexander.Kazakov Avatar asked Oct 17 '22 21:10

Alexander.Kazakov


1 Answers

This is not a retain cycle because self is not what you think it is :)

Properties with initial value are "executed" even before any initializer runs, and for those properties self points to a higher order function of this type:

(TestClass) -> () -> TestClass

So you don't really access the instance, but rather you access a static-like method that does the initialization of all properties that have a default value. This is why you don't have a retain cycle.

addTarget accepts an Any? value for it's first argument, so this violates no type rules so the compiler doesn't complain that you don't pass a NSObject instance there.

Checking the later behaviour - e.g. what happens if the button is added to the UI hierarchy and is tapped, reveals something interesting: the runtime sees that you passed a non-object as a target and sets null values for target and action:

enter image description here

like image 109
Cristik Avatar answered Oct 21 '22 07:10

Cristik