Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Demo that retaining self in a NotificationCenter observer block leads to a reference cycle you have to break manually.
// Paste into a macOS playground in Xcode and run.
// Deleting line 46 "c?.stopObserving()" triggers the assertion because
// the reference cycle caused by retaining self in the observer block is never broken.
import Foundation
let myNotification = NSNotification.Name(rawValue: "myNotification")
var cHasBeenDeallocated = false
class C {
var observer: NSObjectProtocol?
init() {
print(#function)
startObserving()
}
deinit {
print(#function)
cHasBeenDeallocated = true
stopObserving()
}
func startObserving() {
print("Starting listening to notification")
self.observer = NotificationCenter.default.addObserver(forName: myNotification, object: nil, queue: nil) { notification in
print("- Received notification:", notification)
print("- Retaining self in notification block:", self)
}
}
func stopObserving() {
if let observer = self.observer {
print("Stopping listening to notification")
NotificationCenter.default.removeObserver(observer)
self.observer = nil
}
}
}
var c: C? = C()
NotificationCenter.default.post(name: myNotification, object: nil)
// Delete this line to trigger the assertion. deinit is never called because we created a reference cycle.
c?.stopObserving()
c = nil
// If the assertion is triggered, it means we have a reference cycle
assert(cHasBeenDeallocated)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.