Skip to content

Instantly share code, notes, and snippets.

@ole
Last active February 17, 2021 16:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ole/5da89bef2c2cb4e0dd0b9e9a881720a7 to your computer and use it in GitHub Desktop.
Save ole/5da89bef2c2cb4e0dd0b9e9a881720a7 to your computer and use it in GitHub Desktop.
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