Is this a bug, or am I hacking?

  • I have some UI that manages a data model that can store two kinds of
    objects. The data management of the objects is identical but the
    actual objects themselves are fairly distinct. One of the things I'm
    trying to do is to allow my data management UI to update "live" if a
    change is made to the actual object currently displayed. To do this,
    I'm making use of notifications ("object changed"). Works OK as far as
    it goes.

    Here's the hack. The two object classes define different strings for
    the "did change" notification, but my UI management code would prefer
    not to care about what actual object type is in use. So I made the
    literal string constant for one object's message the same as the
    string constant for the other class. As hacks go it's probably fairly
    mild as they both mean much the same and the two object types are
    unlikely to get confused with each other elsewhere.

    But here's what I find. Only notifications from one of the classes is
    actually received. So what I think must be happening is that when
    NSNotificationCenter decides what messages to dispatch to its
    observers, it's only comparing the message name string by address, not
    using -isEqual: or -isEqualToString:, so no match is detected, even
    though the strings are the same. Ah, but aren't identical literal
    string constants coalesced when compiled? Usually, I think that's
    true, but one of these is in a linked framework and the other is local
    to my app, so this is not happening. So should the comparison of the
    notification name by address be considered a bug? Or have I guessed
    wrong about what's going on?

    cheers, Graham
  • On 14.08.2008, at 13:55, Graham Cox wrote:
    > So should the comparison of the notification name by address be
    > considered a bug? Or have I guessed wrong about what's going on?

      AFAIK the pointer comparison is an optimization that's expected to
    happen. That said, if you can't find it in the docs, I'd file a
    documentation bug. What I'd do is either have a shared code module
    containing the notification name, that both access, or just bite the
    bullet and subscribe to both explicitly.

    Cheers,
    -- Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • 8/14/08 5:55 AM, also sprach <graham.cox...>:

    > I have some UI that manages a data model that can store two kinds of
    > objects. The data management of the objects is identical but the
    > actual objects themselves are fairly distinct. One of the things I'm
    > trying to do is to allow my data management UI to update "live" if a
    > change is made to the actual object currently displayed. To do this,
    > I'm making use of notifications ("object changed"). Works OK as far as
    > it goes.
    >
    > Here's the hack. The two object classes define different strings for
    > the "did change" notification, but my UI management code would prefer
    > not to care about what actual object type is in use. So I made the
    > literal string constant for one object's message the same as the
    > string constant for the other class. As hacks go it's probably fairly
    > mild as they both mean much the same and the two object types are
    > unlikely to get confused with each other elsewhere.
    >
    > But here's what I find. Only notifications from one of the classes is
    > actually received. So what I think must be happening is that when
    > NSNotificationCenter decides what messages to dispatch to its
    > observers, it's only comparing the message name string by address, not
    > using -isEqual: or -isEqualToString:, so no match is detected, even
    > though the strings are the same. Ah, but aren't identical literal
    > string constants coalesced when compiled? Usually, I think that's
    > true, but one of these is in a linked framework and the other is local
    > to my app, so this is not happening. So should the comparison of the
    > notification name by address be considered a bug? Or have I guessed
    > wrong about what's going on?

    If I understand your issue correctly, I think you're barking up the wrong
    tree. In my testing, Ic an do:

    NSString *name = [NSString stringWithFormat:@"%@DidChange", @"Dict"];
    NSLog(@"Note name address: %p", name);
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(didChangeNote:) name:name object:nil];

    2008-08-14 10:13:21.375 Tester[4724:813] Note name address: 0x141270

    and if I do:

    NSString *noteName = @"DictDidChange";
    NSLog(@"Note name address: %p", noteName);
    [[NSNotificationCenter defaultCenter] postNotificationName:noteName
    object:self];

    2008-08-14 10:13:21.358 Tester[4724:813] Note name address: 0x304c

    I receive the notification properly, and I can confirm that the notification
    name is a different object in each case.

    Or do I misunderstand?

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • Hi Graham,

    Generally speaking, string constants are only optimised within
    statically linked binaries.  Dynamically linked libraries, such as
    dll's, so's and Mac bundles will not share constants.

    Basically, under the hood, when you link together all of your object
    files at build time, the linker eliminates duplicate constants to save
    on memory.  Obviously, with dynamically linked code, you cannot know
    at link time whether or not a constant is going to be duplicated in
    another code module that will be linked at runtime, so the constant
    has to be included in the binary.  And as it has to be included in the
    binary anyhow, there is no benefit to be had in removing it at
    runtime, and forcing all of the addresses in the binary to be
    recalculated.

    Net result, the same constant will be duplicated in two different
    binary files that are linked dynamically, and hence pointers to those
    constants will have different values in the two different binaries.

    Hope that helps.

    Alli
  • On Thu, Aug 14, 2008 at 7:55 AM, Graham Cox <graham.cox...> wrote:
    > Here's the hack. The two object classes define different strings for the
    > "did change" notification, but my UI management code would prefer not to
    > care about what actual object type is in use. So I made the literal string
    > constant for one object's message the same as the string constant for the
    > other class. As hacks go it's probably fairly mild as they both mean much
    > the same and the two object types are unlikely to get confused with each
    > other elsewhere.

    Well, despite the cause of your problem, if your two classes really
    are conceptually sending the same notification, I would define the
    notification symbol in your application, and have the linked bundle
    reference it as an extern symbol.  If not, then don't make them the
    same notification.  To me, if both classes want to say "something
    changed!", they should be using the same declared notification.  If
    you really want to say "something about foo changed!" and the other to
    say "something about bar changed!", then make them two separate
    symbols.

    --Kyle Sluder
  • On 15 Aug 2008, at 5:21 am, Allison Newman wrote:

    > Hi Graham,
    >
    > Generally speaking, string constants are only optimised within
    > statically linked binaries.  Dynamically linked libraries, such as
    > dll's, so's and Mac bundles will not share constants.
    >
    > Basically, under the hood, when you link together all of your object
    > files at build time, the linker eliminates duplicate constants to
    > save on memory.  Obviously, with dynamically linked code, you cannot
    > know at link time whether or not a constant is going to be
    > duplicated in another code module that will be linked at runtime, so
    > the constant has to be included in the binary.  And as it has to be
    > included in the binary anyhow, there is no benefit to be had in
    > removing it at runtime, and forcing all of the addresses in the
    > binary to be recalculated.
    >
    > Net result, the same constant will be duplicated in two different
    > binary files that are linked dynamically, and hence pointers to
    > those constants will have different values in the two different
    > binaries.
    >

    Thanks, I understand this. That's why NSNotificationCenter apparently
    comparing the notification name by address seems incorrect - identical
    strings could come from anywhere so such a comparison would lead to
    bugs (like mine).

    That said, Keary's test example would seem to contradict my guess
    about what's happening. In my case I simply have:

    NSString* kMyConstant1 = @"some_string";

    and

    NSString* kMyConstant2 = @"some_string";

    where one of these is declared in a linked framework, the other is
    linked locally.

    Doing:

    extern NSString* kMyConstant1;
    extern NSString* kMyConstant2;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:
    (<...>) name:kMyConstant1 object:<theObjectOfInterest>];

    My observer doesn't receive notifications from kMyConstant2 even
    though it's defined to be the same literal string as kMyConstant1.

    As it turns out I can get the functionality I need by simply
    subscribing to both notifications, but I was trying to avoid linking
    in the headers for all the object types that are contained by my data
    management code - the UI for that model doesn't otherwise "need to
    know" what object types they are, as apart from this one notification,
    they conform to a common protocol.

    So this isn't in any way a showstopper - but I'd like to understand
    the nature of this behaviour so that a) I can avoid it again in future
    and b) file a bug if it is one.

    cheers, Graham
  • On Thu, Aug 14, 2008 at 8:45 PM, Graham Cox <graham.cox...> wrote:
    >
    > As it turns out I can get the functionality I need by simply subscribing to
    > both notifications, but I was trying to avoid linking in the headers for all
    > the object types that are contained by my data management code - the UI for
    > that model doesn't otherwise "need to know" what object types they are, as
    > apart from this one notification, they conform to a common protocol.

    Why not make the notification part of the common protocol, if it
    really is a common notification? It's extremely bad to have two
    different symbols for notifications which just *happen* to be equal so
    that you can receive them both with a single method. It would be much
    better to have a single symbol, which makes it obvious what's going
    on.

    > So this isn't in any way a showstopper - but I'd like to understand the
    > nature of this behaviour so that a) I can avoid it again in future and b)
    > file a bug if it is one.

    This is almost certainly a bug in your code somewhere, but without
    seeing it, it's hard to say what it would be.

    Mike
  • 8/14/08 6:45 PM, also sprach <graham.cox...>:

    > [[NSNotificationCenter defaultCenter] addObserver:self selector:
    > (<...>) name:kMyConstant1 object:<theObjectOfInterest>];
    >
    > My observer doesn't receive notifications from kMyConstant2 even
    > though it's defined to be the same literal string as kMyConstant1.
    >
    > As it turns out I can get the functionality I need by simply
    > subscribing to both notifications, but I was trying to avoid linking
    > in the headers for all the object types that are contained by my data
    > management code - the UI for that model doesn't otherwise "need to
    > know" what object types they are, as apart from this one notification,
    > they conform to a common protocol.

    What if we bark up the tree of the "object" parameter? If you are passing
    this parameter, you will only receive notifications from the specific
    object. It sounds like you are working with two different objects that you
    want to receive notifications for. In this case, you will have to pass nil
    for the object, and possibly test the object for interest in your
    notification method.

    Best,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On 15 Aug 2008, at 11:53 pm, Keary Suska wrote:

    > What if we bark up the tree of the "object" parameter? If you are
    > passing
    > this parameter, you will only receive notifications from the specific
    > object. It sounds like you are working with two different objects
    > that you
    > want to receive notifications for. In this case, you will have to
    > pass nil
    > for the object, and possibly test the object for interest in your

    OK red-face time. I started to write a lengthy reply with the actual
    code and so forth, and in so doing found the real bug. And of course,
    it's in my code, where else ;-)

    Typically I do this:

    // header:

    extern NSString* kSomeConstant;

    // .m

    NSString* kSomeConstant = @"kSomeConstant";

    That is, the actual string is literally the same characters as the
    constant itself, which appears to be the convention in Cocoa also. In
    fact I do this so automatically that I *assumed* that the constant in
    the framework was defined that way, so when I "aliased" the constant
    in my local code, I just used the characters of the first constant.
    However, for reasons which have to do with changing names of various
    things in the framework over time, there are a handful of constants
    that are not literally the same as their actual character strings, and
    this is one of them. So the bug is blindingly obvious - the strings
    are not the same. D'oh!

    Had I done this:

    #define kMyAliasedConstant  kSomeConstant

    I would have been OK.

    Anyway, it's been useful to discuss the issue because I think the idea
    of trying to make one notification look like another isn't such a
    great one after all, so thanks to all who helped.

    cheers, Graham
previous month august 2008 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 29 30 31
Go to today