Notification issue in Combine

I do a simple notification test with combine.
Click postNotification button in ViewController that received notification successful.
Click removeReceiver button (set _receiver to nil) in ViewController that NotificationReceiver object never deinit. Why?
Do I need to do more for releasing NotificationReceiver object in ViewController?
Could you give me some advice. thanks

thanks

class NotificationReceiver {
    var _observer: AnyCancellable?
    
    deinit {
        print("[NotificationReceiver] deinit")
    }
    
    init() {
        _observer = NotificationCenter.default.publisher(for: .userClick, object: nil)
            .sink { notification in
                print("Received notification: \(notification)")
            }
    }
}

extension Notification.Name {
    static let userClick = Notification.Name("UserClick")
}
class ViewController: NSViewController {
    var _receiver: NotificationReceiver? = NotificationReceiver()
    
    @IBAction func postNotification(_ sender: Any) {
        NotificationCenter.default.post(name: .userClick, object: nil)
    }
    
    @IBAction func removeReceiver(_ sender: Any) {
        _receiver = nil
    }
}

Hi @diuming! Thank you for sharing your code and sorry to hear that you’re running into an issue. Looking at your code it seems as though the NotificationReceiver object is never deinitialized because the _observer property holds a strong preference to the Combine publisher. You can try to cancel the _observer subscription when you set the _receiver property to nil. Calling the cancel( ) method on the _observer property may do the trick. Here is an example with your code,

class ViewController: NSViewController {
    var _receiver: NotificationReceiver? = NotificationReceiver()
    
    @IBAction func postNotification(_ sender: Any) {
        NotificationCenter.default.post(name: .userClick, object: nil)
    }
    
    @IBAction func removeReceiver(_ sender: Any) {
        _observer?.cancel()
        _receiver = nil
    }
}

Something else you can try is capturing a weak reference to self in the closure, which should break the retain cycle and deallocating the NotificationReceiver.

class NotificationReceiver {
    var _observer: AnyCancellable?

    deinit {
        print("[NotificationReceiver] deinit")
    }

    init() {
        _observer = NotificationCenter.default.publisher(for: .userClick, object: nil)
            .sink { [weak self] notification in
                print("Received notification: \(notification)")
            }
    }
}

Then you can call _receiver = nil in the view controller. Hope this helps!

2 Likes

Hi gdelarosa,

I forget strong preference in closure.
sink { [weak self] notification in is better for me.

thanks

2 Likes

This topic was automatically closed after 166 days. New replies are no longer allowed.