Objective-C
NSNotification
observer removal
iOS development
memory management

Objective-C Where to remove observer for NSNotification?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

In Objective-C, every call to addObserver: on NSNotificationCenter must be balanced with a corresponding removeObserver: to prevent crashes and memory leaks. The best place to remove observers depends on the object's lifecycle: for view controllers, use dealloc (or viewWillDisappear: if the observer is view-lifecycle-dependent). Since iOS 9, NSNotificationCenter automatically removes observers when the object is deallocated for name-based registrations, but block-based observers still require manual removal. Understanding these rules prevents the two most common notification bugs: messages sent to deallocated objects, and duplicate observer registrations.

Adding and Removing Observers

objc
1// Adding an observer (typically in viewDidLoad or init)
2- (void)viewDidLoad {
3    [super viewDidLoad];
4    [[NSNotificationCenter defaultCenter] addObserver:self
5                                             selector:@selector(handleNotification:)
6                                                 name:@"MyNotification"
7                                               object:nil];
8}
9
10// Removing in dealloc (recommended for most cases)
11- (void)dealloc {
12    [[NSNotificationCenter defaultCenter] removeObserver:self];
13}
14
15// Handling the notification
16- (void)handleNotification:(NSNotification *)notification {
17    NSLog(@"Received: %@", notification.userInfo);
18}

removeObserver:self in dealloc removes all notification registrations for this object, regardless of notification name or sender.

Where to Remove: dealloc vs viewWillDisappear

objc
1// Option 1: dealloc — remove when object is destroyed
2- (void)dealloc {
3    [[NSNotificationCenter defaultCenter] removeObserver:self];
4}
5
6// Option 2: viewWillDisappear — remove when view leaves screen
7- (void)viewWillDisappear:(BOOL)animated {
8    [super viewWillDisappear:animated];
9    [[NSNotificationCenter defaultCenter] removeObserver:self
10                                                    name:@"MyNotification"
11                                                  object:nil];
12}
13
14// Re-register in viewWillAppear
15- (void)viewWillAppear:(BOOL)animated {
16    [super viewWillAppear:animated];
17    [[NSNotificationCenter defaultCenter] addObserver:self
18                                             selector:@selector(handleNotification:)
19                                                 name:@"MyNotification"
20                                               object:nil];
21}

Use dealloc when the observer should live as long as the object exists. Use viewWillAppear/viewWillDisappear when the observer should only be active while the view is on screen (e.g., to avoid updating a hidden view controller's UI).

Block-Based Observers (Require Manual Removal)

objc
1@property (nonatomic, strong) id notificationToken;
2
3- (void)viewDidLoad {
4    [super viewDidLoad];
5    self.notificationToken = [[NSNotificationCenter defaultCenter]
6        addObserverForName:@"MyNotification"
7                    object:nil
8                     queue:[NSOperationQueue mainQueue]
9                usingBlock:^(NSNotification *note) {
10                    NSLog(@"Received: %@", note.userInfo);
11                }];
12}
13
14- (void)dealloc {
15    // MUST remove block-based observers manually
16    if (self.notificationToken) {
17        [[NSNotificationCenter defaultCenter] removeObserver:self.notificationToken];
18        self.notificationToken = nil;
19    }
20}

Block-based observers return an opaque token object. This token — not self — is the observer. You must store it and remove it explicitly. The iOS 9 automatic cleanup does not apply to block-based observers.

iOS 9+ Automatic Cleanup

Starting with iOS 9, NSNotificationCenter uses weak references for selector-based observers. When the observer object is deallocated, the notification center automatically removes the registration:

objc
1// iOS 9+: this is safe even without removeObserver: in dealloc
2[[NSNotificationCenter defaultCenter] addObserver:self
3                                         selector:@selector(handleNotification:)
4                                             name:@"MyNotification"
5                                           object:nil];
6
7// But explicit removal is still recommended because:
8// 1. Block-based observers are NOT auto-cleaned
9// 2. You may want to stop receiving notifications before dealloc
10// 3. Pairing add/remove makes the code self-documenting

Swift Equivalent

swift
1// Swift — modern NotificationCenter API
2class MyViewController: UIViewController {
3    private var token: NSObjectProtocol?
4
5    override func viewDidLoad() {
6        super.viewDidLoad()
7        token = NotificationCenter.default.addObserver(
8            forName: Notification.Name("MyNotification"),
9            object: nil,
10            queue: .main
11        ) { [weak self] notification in
12            self?.handleNotification(notification)
13        }
14    }
15
16    deinit {
17        if let token = token {
18            NotificationCenter.default.removeObserver(token)
19        }
20    }
21
22    private func handleNotification(_ notification: Notification) {
23        print("Received: \(notification.userInfo ?? [:])")
24    }
25}

Note the [weak self] capture list in the block — without it, the block retains self, creating a retain cycle that prevents deinit from ever being called.

Removing Specific vs All Observers

objc
1// Remove all observers for this object (any name, any sender)
2[[NSNotificationCenter defaultCenter] removeObserver:self];
3
4// Remove only a specific notification
5[[NSNotificationCenter defaultCenter] removeObserver:self
6                                                name:@"MyNotification"
7                                              object:nil];
8
9// Remove for a specific sender
10[[NSNotificationCenter defaultCenter] removeObserver:self
11                                                name:@"MyNotification"
12                                              object:specificSender];

Using removeObserver:self without specifying a name removes all registrations, which is safe in dealloc but can accidentally remove observers registered by superclasses or categories if called elsewhere.

Common Pitfalls

  • Not removing block-based observers: Block-based observers (created with addObserverForName:object:queue:usingBlock:) are not automatically cleaned up on iOS 9+. The returned token must be stored and explicitly removed in dealloc/deinit, or the block continues to execute after the intended receiver is gone.
  • Creating a retain cycle with block-based observers: The block captures self strongly by default. If self also holds a strong reference to the token, neither can be deallocated. Use __weak typeof(self) weakSelf = self in Objective-C or [weak self] in Swift inside the block.
  • Registering observers multiple times without removing: Calling addObserver: in viewWillAppear: without a matching removeObserver: in viewWillDisappear: registers a new observer each time the view appears. The notification handler fires multiple times — once per registration.
  • Calling removeObserver:self outside of dealloc: Removing all observers with removeObserver:self in methods like viewWillDisappear: can accidentally unregister observers added by parent classes, UIKit internals, or categories. Remove specific notifications by name instead.
  • Assuming iOS 9 auto-cleanup handles everything: While iOS 9+ automatically removes selector-based observers at dealloc, explicit removal is still a best practice. It makes the code self-documenting, handles block-based observers, and ensures observers stop before dealloc if needed.

Summary

  • Remove selector-based observers in dealloc with [[NSNotificationCenter defaultCenter] removeObserver:self]
  • Remove block-based observers by storing the returned token and calling removeObserver:token in dealloc
  • Use viewWillAppear:/viewWillDisappear: pairs when the observer should only be active while the view is visible
  • iOS 9+ automatically cleans up selector-based observers, but block-based observers must always be removed manually
  • Use [weak self] (Swift) or __weak (Objective-C) in notification blocks to prevent retain cycles
  • Prefer removing specific notifications by name over blanket removeObserver:self outside of dealloc

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.