NSNotificationCenter : multiple messages sent to the same observer?

  • I just discovered something recently. If you register an observer
    with the same name/object/selector twice, you get the notification
    twice when you post it.

    Isn't the NSNotificationCenter supposed to prevent this?
  • On Sat, Feb 21, 2009 at 6:06 PM, Iceberg-Dev <dev.iceberg...> wrote:
    > I just discovered something recently. If you register an observer with the
    > same name/object/selector twice, you get the notification twice when you
    > post it.
    >
    > Isn't the NSNotificationCenter supposed to prevent this?

    Why, is there some place in the documentation where it says that?

    Mike
  • Le 22 févr. 09 à 00:06, Iceberg-Dev a écrit :

    > I just discovered something recently. If you register an observer
    > with the same name/object/selector twice, you get the notification
    > twice when you post it.
    >
    > Isn't the NSNotificationCenter supposed to prevent this?

    Notifications post via a NSNotificationQueue can have this behaviour.
    The documentation said nothing about coalescing notifications sent
    directly to a NSNotificationCenter.

    Frédéric
  • Le 22 févr. 09 à 10:52, Frédéric Testuz a écrit :

    > Le 22 févr. 09 à 00:06, Iceberg-Dev a écrit :
    >
    >> I just discovered something recently. If you register an observer
    >> with the same name/object/selector twice, you get the notification
    >> twice when you post it.
    >>
    >> Isn't the NSNotificationCenter supposed to prevent this?
    >
    > Notifications post via a NSNotificationQueue can have this
    > behaviour. The documentation said nothing about coalescing
    > notifications sent directly to a NSNotificationCenter.

    Sorry, after reflection, I made a confusion between sending two time a
    notification and registering two times for a notification.

    Frédéric
  • On Feb 22, 2009, at 1:37 AM, Michael Ash wrote:

    > On Sat, Feb 21, 2009 at 6:06 PM, Iceberg-Dev
    > <dev.iceberg...> wrote:
    >> I just discovered something recently. If you register an observer
    >> with the
    >> same name/object/selector twice, you get the notification twice
    >> when you
    >> post it.
    >>
    >> Isn't the NSNotificationCenter supposed to prevent this?
    >
    > Why, is there some place in the documentation where it says that?

    There isn't. But it would make sense in 99% of cases.
  • On Feb 22, 2009, at 8:59 AM, Iceberg-Dev wrote:
    > On Feb 22, 2009, at 1:37 AM, Michael Ash wrote:
    >> On Sat, Feb 21, 2009 at 6:06 PM, Iceberg-Dev
    >> <dev.iceberg...> wrote:
    >>> I just discovered something recently. If you register an observer
    >>> with the
    >>> same name/object/selector twice, you get the notification twice
    >>> when you
    >>> post it.
    >>>
    >>> Isn't the NSNotificationCenter supposed to prevent this?
    >>
    >> Why, is there some place in the documentation where it says that?
    >
    > There isn't. But it would make sense in 99% of cases.

    Actually, the behavior is documented in the Cocoa Notifications
    programming topic:
    > It is possible for an observer to register to receive more than one
    > message for the same notification. In such a case, the observer will
    > receive all messages it is registered to receive for the
    > notification, but the order in which it receives them cannot be
    > determined.
    >
    The current behavior makes it possible to implement architectures
    where a client could take "more action" for any given notification by
    registering more than once.  Not supporting this pattern would make
    such a pattern significantly more inconvenient to implement.

    As well, any kind of a coalescing in the notification center is going
    to increase the complexity/overhead of the observer and, in
    particularly, might likely impact notification delivery speed (or,
    alternatively, require the notification center to use a bunch more
    memory to differentiate between "set of registered observers" and
    "list of observers that I actually deliver stuff to").

    b.bum
  • On Feb 22, 2009, at 6:39 PM, Bill Bumgarner wrote:

    > On Feb 22, 2009, at 8:59 AM, Iceberg-Dev wrote:
    >> On Feb 22, 2009, at 1:37 AM, Michael Ash wrote:
    >>> On Sat, Feb 21, 2009 at 6:06 PM, Iceberg-Dev
    >>> <dev.iceberg...> wrote:
    >>>> I just discovered something recently. If you register an
    >>>> observer with the
    >>>> same name/object/selector twice, you get the notification twice
    >>>> when you
    >>>> post it.
    >>>>
    >>>> Isn't the NSNotificationCenter supposed to prevent this?
    >>>
    >>> Why, is there some place in the documentation where it says that?
    >>
    >> There isn't. But it would make sense in 99% of cases.
    >
    > Actually, the behavior is documented in the Cocoa Notifications
    > programming topic:
    >> It is possible for an observer to register to receive more than
    >> one message for the same notification. In such a case, the
    >> observer will receive all messages it is registered to receive for
    >> the notification, but the order in which it receives them cannot
    >> be determined.

    Thanks for the pointer. It would be nice to have this explanation
    available in the description of the addObserver:selector:name:object:
    as it might be where one could expect to find it. I filed an
    enhancement suggestion.

    > The current behavior makes it possible to implement architectures
    > where a client could take "more action" for any given notification
    > by registering more than once.  Not supporting this pattern would
    > make such a pattern significantly more inconvenient to implement.
    >
    > As well, any kind of a coalescing in the notification center is
    > going to increase the complexity/overhead of the observer and, in
    > particularly, might likely impact notification delivery speed (or,
    > alternatively, require the notification center to use a bunch more
    > memory to differentiate between "set of registered observers" and
    > "list of observers that I actually deliver stuff to").

    I think it would just require to make the search once on
    registration, not every time a notification is sent.
  • On Feb 22, 2009, at 9:59 AM, Iceberg-Dev wrote:
    > Thanks for the pointer. It would be nice to have this explanation
    > available in the description of the
    > addObserver:selector:name:object: as it might be where one could
    > expect to find it. I filed an enhancement suggestion.

    But then it would have to be repeated on -postNotification: and -
    removeObserver:, etc...

    The documentation, in general, tries to avoid repeating stuff.
    Always click through to the programming topic as it gives the overall
    design philosophy and usage patterns for the given class.

    >> The current behavior makes it possible to implement architectures
    >> where a client could take "more action" for any given notification
    >> by registering more than once.  Not supporting this pattern would
    >> make such a pattern significantly more inconvenient to implement.
    >>
    >> As well, any kind of a coalescing in the notification center is
    >> going to increase the complexity/overhead of the observer and, in
    >> particularly, might likely impact notification delivery speed (or,
    >> alternatively, require the notification center to use a bunch more
    >> memory to differentiate between "set of registered observers" and
    >> "list of observers that I actually deliver stuff to").
    >
    > I think it would just require to make the search once on
    > registration, not every time a notification is sent.

    Assuming that your statement is made in light of trying to avoid the
    bloat I alluded to in the previous paragraph, this would lead to some
    very surprising behavior.

    Imagine this sequence of calls...

    - (void)addObserver:self selector:(SEL)aSelector
    name:BobsYourUncleNotification object:(id)anObject;
    - (void)addObserver:self selector:(SEL)aSelector
    name:BobsYourUncleNotification object:(id)anObject;
    - (void)removeObserver:self name:BobsYourUncleNotification object:
    (id)anObject;

    If the notification center coalesced observers on registration, the
    above would mean that the notification would no longer be sent.

    b.bum
  • On Feb 22, 2009, at 7:06 PM, Bill Bumgarner wrote:

    > On Feb 22, 2009, at 9:59 AM, Iceberg-Dev wrote:
    >> [...]
    >>> The current behavior makes it possible to implement architectures
    >>> where a client could take "more action" for any given
    >>> notification by registering more than once.  Not supporting this
    >>> pattern would make such a pattern significantly more inconvenient
    >>> to implement.
    >>>
    >>> As well, any kind of a coalescing in the notification center is
    >>> going to increase the complexity/overhead of the observer and, in
    >>> particularly, might likely impact notification delivery speed
    >>> (or, alternatively, require the notification center to use a
    >>> bunch more memory to differentiate between "set of registered
    >>> observers" and "list of observers that I actually deliver stuff
    >>> to").
    >>
    >> I think it would just require to make the search once on
    >> registration, not every time a notification is sent.
    >
    > Assuming that your statement is made in light of trying to avoid
    > the bloat I alluded to in the previous paragraph, this would lead
    > to some very surprising behavior.
    >
    > Imagine this sequence of calls...
    >
    > - (void)addObserver:self selector:(SEL)aSelector
    > name:BobsYourUncleNotification object:(id)anObject;
    > - (void)addObserver:self selector:(SEL)aSelector
    > name:BobsYourUncleNotification object:(id)anObject;
    > - (void)removeObserver:self name:BobsYourUncleNotification object:
    > (id)anObject;
    >
    > If the notification center coalesced observers on registration, the
    > above would mean that the notification would no longer be sent.

    If anObject is the same object in the 3 calls, this is probably
    already the case if I am to believe the documentation. Which is the
    behavior I would expect/wish for.
  • On Feb 22, 2009, at 10:23 AM, Iceberg-Dev wrote:
    >> If the notification center coalesced observers on registration, the
    >> above would mean that the notification would no longer be sent.
    >
    > If anObject is the same object in the 3 calls, this is probably
    > already the case if I am to believe the documentation. Which is the
    > behavior I would expect/wish for.

    And you'd be correct.  I tested it (see below).

    [Session started at 2009-02-22 10:43:24 -0800.]
    2009-02-22 10:43:24.137 foobar[11347:10b] Notif: {
        times = 2;
    }
    2009-02-22 10:43:24.142 foobar[11347:10b] Notif: {
        times = 2;
    }

    The Debugger has exited with status 0.

    So... it'd appear that NSNotificationCenter is implemented entirely
    around being as small and fast as possible.  "Remove" removes
    everything matching and "add" adds without consideration.  No stack
    or reference counting involved.

    And you are correct that the documentation says that: "Removes
    matching entries from the receiver’s dispatch table."  Which, given
    the minimal and similar description on -addObserver: would imply that
    it adds the same without consideration for what is already in the
    center.

    b.bum
    (The code below assumes GC is on, obviously.  And it is only written
    to test the question at hand -- not to even remotely be a correct
    pattern of implementation).

    #import <Foundation/Foundation.h>

    #define BobsYourUncle @"BobsYourUncle"

    @interface Foo: NSObject

    @end

    @implementation Foo
    - (void) bob: (NSNotification *) aNotif
    {
        NSLog(@"Notif: %@", [aNotif userInfo]);
    }

    - init
    {
        NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
        NSNotification *n;
        [super init];
        [dc addObserver:self selector:@selector(bob:) name:BobsYourUncle
    object:self];
        [dc addObserver:self selector:@selector(bob:) name:BobsYourUncle
    object:self];
        n = [NSNotification notificationWithName:BobsYourUncle
    object:self userInfo: [NSDictionary dictionaryWithObject:@"2"
    forKey:@"times"]];
        [dc postNotification: n];
        [dc removeObserver:self name:BobsYourUncle object:self];
        n = [NSNotification notificationWithName:BobsYourUncle
    object:self userInfo: [NSDictionary dictionaryWithObject:@"1"
    forKey:@"times"]];
        [dc postNotification: n];
        return self;
    }
    @end

    int main (int argc, const char * argv[]) {
        objc_startCollectorThread();
        [Foo new];
        return 0;
    }
previous month february 2009 next month
MTWTFSS
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28  
Go to today