1

It appears I am getting a Strong Reference Cycle when using the NotifcationCenter.

I am using NotificationCenter to observe the Rotation of the device. (While some would argue this is not the best way to determine the device rotation, this currently seems to be my only route, as no autolayout is being used and storyboard is not being used).

deinit {} is never being called in my ViewController even if I remove the observer in viewWillDisappear and viewDidDisappear.

import UIKit

class TestVC: UIViewController {


    deinit {
        print("TestClass Deinit") //not being triggered ever
    }

    @objc private func rotationDetected(sender: Any) {
        print("we rotated")
    }

    override func viewDidDisappear(_ animated: Bool) {
        //NotificationCenter.default.removeObserver(UIDevice.orientationDidChangeNotification)
    }
    override func viewWillDisappear(_ animated: Bool) {
        NotificationCenter.default.removeObserver(UIDevice.orientationDidChangeNotification)
    //NotificationCenter.default.removeObserver(self) //also doesn't work

    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)

    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }


}

Any ideas as to why this is occurring and how to resolve it?

Also open to any new ideas on how to determine rotation detection else ways (no autolayout or storyboard is being used though).

To reach TestVC() I used self.navigationController?.pushViewController(TestVC(), animated: true) in the previous ViewController and to go back I use the pop.

Without the Observer present, the class will correctly deinit.

RESOLVED

Thanks to the answer marked below the Strong Reference Cycle is removed.

Simply replace NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)

with

NotificationCenter.default.addObserver(self, selector: #selector(rotationDetected), name: UIDevice.orientationDidChangeNotification, object: nil)

AndrewS
  • 351
  • 4
  • 18

2 Answers2

1

This should work in viewWillDisappear:

NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)

in combination with using the following in viewWillAppear:

NotificationCenter.default.addObserver(self, selector: #selector(rotationDetected), name: UIDevice.orientationDidChangeNotification, object: nil)
10623169
  • 984
  • 1
  • 12
  • 22
  • Any idea as to why the `addObserver` you provided would not cause a Strong Reference Cycle, but the one from the OP does? – AndrewS Jan 16 '20 at 16:30
  • 1
    Not sure why this then got downvoted after being accepted and upvoted. This is the correct answer. OP I think it is because you are implicitly retaining self in the closure `using block: @escaping (Notification) -> Void)` of that method – 10623169 Jan 16 '20 at 16:40
0

your method of removing observer is incorrect, you should do like this:

class TestVC {
    private var observer: Any

    func viewWillAppear() {
        observer = NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main, using: rotationDetected)
    }

    func viewWillDisappear() {
        NotificationCenter.default.removeObserver(observer)
    }
}

to eliminate the Strong Reference Cycle, use weak in the closure.

JIE WANG
  • 1,875
  • 1
  • 17
  • 27