Frage

So, I have an object that has methods to toggle watching for a certain notification name, like so:

- (void)startWatchingForA
{
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(handleA:)
                                                 name: SomeNotificationName
                                               object: nil];
}

- (void)stopWatchingForA
{
    [[NSNotificationCenter defaultCenter] removeObserver: self
                                                    name: SomeNotificationName
                                                  object: nil];
}

Which works fine. However, I have another method, handleB:, that needs to respond to the same notification.

- (void)startWatchingForB
{
    [[NSNotificationCenter defaultCenter] addObserver: self
                                             selector: @selector(handleB:)
                                                 name: SomeNotificationName
                                               object: nil];
}

- (void)stopWatchingForB
{
    [[NSNotificationCenter defaultCenter] removeObserver: self
                                                    name: SomeNotificationName
                                                  object: nil];
}

The issue is that, should stopWatchingA or stopWatchingB be called, the object will stop watching for both. Is there some way to remove one observation instance, but not the other?

Ideally, when I call stopWatchingForA, I want handleA: to not be called, entirely independent of B.

War es hilfreich?

Lösung

How about a slight redesign? Only one method to receive the notification, and two flags to indicate what you want to do with it.

@implementation ThisObject
{
   BOOL isWatchingForA;
   BOOL isWatchingForB;
}

- (void) registerForNotifications {

    [[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(handleNotification:)
                                             name: SomeNotificationName
                                           object: nil];
}

- (void) deregisterForNotifications {
    if( !isWatchingA && !isWatchingB ){
        [[NSNotificationCenter defaultCenter] removeObserver: self
                                                name: SomeNotificationName
                                              object: nil];
    }
}


- (void) startWatchingForA {
    isWatchingForA = YES;
}
- (void) stopWatchingForA {
    isWatchingForA = NO;
}

- (void) startWatchingForB {
    isWatchingForB = YES;
}
- (void) stopWatchingForB {
    isWatchingForB = NO;
}

- (void) handleNotification: (NSNotification *)note {

    if( isWatchingForA ){
        [self handleA:note];
    }
    if( isWatchingB ){
        [self handleB:note];
    }
}

//...

@end

Andere Tipps

So blocks and mikeash, once again, ended up saving my day!

One thing that I omitted in my original question was that I would like for this paradigm to be category-safe, which meant no ivars or properties as flags. Here's what I ended up doing:

#import <objc/runtime.h>

static void *AHandlerKey;
static void *BHandlerKey;

- (void)startWatchingForA
{
    // initialize `void (^aBlock)(NSNotification *)` block
    id AHandler = [[NSNotificationCenter defaultCenter] addObserverForName: SomeNotificationName
                                                                    object: nil
                                                                     queue: nil
                                                                usingBlock: aBlock];

    objc_setAssociatedObject(self, AHandlerKey, AHandler, OBC_ASSOCIATION_RETAIN);
}

- (void)stopWatchingForA
{
    id AHandler = objc_getAssociatedObject(self, AHandlerKey);
    [[NSNotificationCenter defaultCenter] removeObserver: AHandler
                                                    name: SomeNotificationName
                                                  object: nil];
}

- (void)startWatchingForB
{
    // initialize `void (^bBlock)(NSNotification *)` block
    id BHandler = [[NSNotificationCenter defaultCenter] addObserverForName: SomeNotificationName
                                                                    object: nil
                                                                     queue: nil
                                                                usingBlock: bBlock];

    objc_setAssociatedObject(self, BHandlerKey, BHandler, OBC_ASSOCIATION_RETAIN);
}

- (void)stopWatchingForB
{
    id BHandler = objc_getAssociatedObject(self, BHandlerKey);
    [[NSNotificationCenter defaultCenter] removeObserver: BHandler
                                                    name: SomeNotificationName
                                                  object: nil];
}

This way, AHandler receives notifications, while aBlock is executed, and BHandler/bBlock likewise. When I remove AHandler as an observer, BHandler is unaffected, and vice versa. Perfect!

Update: Big thanks to Josh Caswell who suggested the use of object association!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top