KVO and the observeValueForKeyPath bottleneck

  • I'm just curious about how people are handling the fact that when you do
    KVO, all your notifications are bottlenecked through a single method,
    observeValueForKeyPath:... This is a very unpleasant and crude architecture
    (in contrast to NSNotification where, when a notification comes in, it is
    automatically routed to the selector of your choice). I really don't want a
    series of "ifs" here. I can imagine a simple dispatcher architecture based
    on NSSelectorFromString; is this the sort of thing people are using? Thx -
    m.
    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • Hi Matt,
    Yeah I'm actually doing something similar though I wasn't aware of
    NSSelectorFromString.

    For a custom view that responds to a few dozen attributes of the
    controller's current selection....

    - (void)observeValueForKeyPath:(NSString *)keyPath
                         ofObject:(id)object
                            change:(NSDictionary *)change
                            context:(void *)context
    {
    NSString *keyPathName;
    if ([keyPath isEqualToString:@"selection.bgImage.color"] == YES){
      // handle special case of this attribute of a relationship
      keyPathName = @"bgImageColor";
    } else {
      // convert "selection.attribute" keypath to appropriate accessor
    name -- by abusing pathExtension method :)
      keyPathName = [keyPath pathExtension];
    }
    [self setValue:[object valueForKeyPath:keyPath] forKey:keyPathName];
    }

    So observing "selection.attribute" will call my setAttribute accessor
    with the new value via KVC.

    Possibly not the most proper setup, but it seems to work well enough
    for this view's situation.

    George
  • On Jul 14, 2006, at 1:44 PM, Matt Neuburg wrote:

    > I'm just curious about how people are handling the fact that when
    > you do
    > KVO, all your notifications are bottlenecked through a single method,
    > observeValueForKeyPath:... This is a very unpleasant and crude
    > architecture
    > (in contrast to NSNotification where, when a notification comes in,
    > it is
    > automatically routed to the selector of your choice). I really
    > don't want a
    > series of "ifs" here. I can imagine a simple dispatcher
    > architecture based
    > on NSSelectorFromString; is this the sort of thing people are
    > using? Thx -
    > m.
    > --

    Good question; I hope this sparks more discussion.

    I can think of two approaches using the context parameter, one of
    which I implemented recently.  In my main controller I am observing
    several array controllers, where the content object types vary from
    controller to controller.  When the selection on any controller
    changes, I get the path to a file and display it in a viewer.  When
    registering as an observer, I set the context to a string constant
    for the key path to the file name.  Here is a simplified example:

    -(void)awakeFromNib
    {
    [inputFilesArrayController addObserver:self forKeyPath:@"selection"
    options:nil context:@"selection.sourceFilePath"];
    [processedFilesArrayController addObserver:self
    forKeyPath:@"selection" options:nil context:@"selection.savedToPath"];
    }

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
    // In awakeFromNib:, KVO registration done with context set to the
    key value to retrieve.
    // If it is a string (thus a path), then display it.
    if (context) {
      id displayPath = [object valueForKeyPath:context];

      // We may get an placeholder marker or nil
      if (displayPath && [displayPath isKindOfClass:[NSString class]]) {
      [fileViewerController open:displayPath];
      }
    }
    }

    This worked OK for the given situation.  If I had a more complex
    scenario, I would consider enumerating constants to use as the
    context and then use a switch() in observeValueForKeyPath:.
    Something like this:

    -(void)awakeFromNib
    {
    [firstArrayController addObserver:self
    forKeyPath:@"selection.attribute" options:nil context:EnumConstant1];
    [secondArrayController addObserver:self
    forKeyPath:@"selection.attribute" options:nil context:EnumConstant2];
    }

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
    switch (context) {
      case EnumConstant1:
      // ...do something...
      break;
      case EnumConstant2:
      // ...do something else...
      break;
      default:
      break;
    }
    }

    I haven't tried the second example, so it may not be as suitable as I
    think.  Comments?

    Cheers,

    Aaron
  • On 14/07/2006, at 19.44, Matt Neuburg wrote:

    > I'm just curious about how people are handling the fact that when
    > you do
    > KVO, all your notifications are bottlenecked through a single method,
    > observeValueForKeyPath:... This is a very unpleasant and crude
    > architecture
    > (in contrast to NSNotification where, when a notification comes in,
    > it is
    > automatically routed to the selector of your choice). I really
    > don't want a
    > series of "ifs" here. I can imagine a simple dispatcher
    > architecture based
    > on NSSelectorFromString; is this the sort of thing people are
    > using? Thx -
    > m.

    Here is what I am currently using:

    - (void)awakeFromNib
    {
        [myController addObserver:self forKeyPath:@"selectedObjects"
    options:0 context:@selector(selectionChanged:)];
    }

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
        if ([self respondsToSelector:(SEL)context])
            [self performSelector:(SEL)context withObject:change];
    }

    Just be really careful to always use a context that is a selector,
    otherwise you will probably crash. [NSObject respondsToSelector:]
    will probably work for a pointer that is not a selector, but I
    wouldn't count on it.

    You can of course pass the object or keyPath as well if you need it.
    Unfortunately there is no [NSObject
    performSelector:withObject:withObject:withObject:] if you need all
    three arguments.
  • On 17/07/2006, at 12.38, Jakob Olesen wrote:

    > - (void)awakeFromNib
    > {
    > [myController addObserver:self forKeyPath:@"selectedObjects"
    > options:0 context:@selector(selectionChanged:)];
    > }
    >
    > - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    > object change:(NSDictionary *)change context:(void *)context
    > {
    > if ([self respondsToSelector:(SEL)context])
    > [self performSelector:(SEL)context withObject:change];
    > }

    Sorry to follow up on myself, but it just occurred to me that the
    information fits perfectly in an NSNotification:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
        [self performSelector:(SEL)context withObject:[NSNotification
    notificationWithName:keyPath object:object userInfo:change]];
    }

    The method you dispatch to is then identical to a notification
    handler. (You could even use it for both in simple cases)
  • On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen" <stoklund...>:

    > - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    > object change:(NSDictionary *)change context:(void *)context
    > {
    > if ([self respondsToSelector:(SEL)context])
    > [self performSelector:(SEL)context withObject:change];
    > }

    By an incredible coincidence, yesterday (before this reponse arrived at my
    desk), after considering all the responses I'd gotten, I decided upon an
    approach and implemented it in my app, and it was word for word, letter for
    letter identical with this. m.

    --
    matt neuburg, phd = <matt...>, http://www.tidbits.com/matt/
    pantes anthropoi tou eidenai oregontai phusei
    AppleScript: the Definitive Guide - Second Edition!
    http://www.amazon.com/gp/product/0596102119
    Take Control of Word 2004, Tiger, and more -
    http://www.takecontrolbooks.com/tiger-customizing.html
    Subscribe to TidBITS! It's free and smart. http://www.tidbits.com/
  • On 17/07/2006, at 16.38, Matt Neuburg wrote:

    > On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen"
    > <stoklund...>:
    >
    >> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    >> object change:(NSDictionary *)change context:(void *)context
    >> {
    >> if ([self respondsToSelector:(SEL)context])
    >> [self performSelector:(SEL)context withObject:change];
    >> }
    >
    > By an incredible coincidence, yesterday (before this reponse
    > arrived at my
    > desk), after considering all the responses I'd gotten, I decided
    > upon an
    > approach and implemented it in my app, and it was word for word,
    > letter for
    > letter identical with this. m.

    Cool, then I can get the SCO lawyers to help me sue you :-)

    Seriously, is there any reason for checking respondsToSelector?
    Wouldn't it be better to get an exception (or crash) if you mistype
    the selector or somebody else registers you as an observer?
  • On or about 7/17/06 7:57 AM, thus spake "Jakob Olesen" <stoklund...>:

    >
    > On 17/07/2006, at 16.38, Matt Neuburg wrote:
    >
    >> On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen"
    >> <stoklund...>:
    >>
    >>> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    >>> object change:(NSDictionary *)change context:(void *)context
    >>> {
    >>> if ([self respondsToSelector:(SEL)context])
    >>> [self performSelector:(SEL)context withObject:change];
    >>> }
    >>
    >> By an incredible coincidence, yesterday (before this reponse
    >> arrived at my
    >> desk), after considering all the responses I'd gotten, I decided
    >> upon an
    >> approach and implemented it in my app, and it was word for word,
    >> letter for
    >> letter identical with this. m.
    >
    > Cool, then I can get the SCO lawyers to help me sue you :-)
    >
    > Seriously, is there any reason for checking respondsToSelector?
    > Wouldn't it be better to get an exception (or crash) if you mistype
    > the selector or somebody else registers you as an observer?

    No. That's not how notification (or, for that matter, delegation) behaves.
    Both those behaviors are considered perfectly acceptable. m.

    --
    matt neuburg, phd = <matt...>, http://www.tidbits.com/matt/
    pantes anthropoi tou eidenai oregontai phusei
    AppleScript: the Definitive Guide - Second Edition!
    http://www.amazon.com/gp/product/0596102119
    Take Control of Word 2004, Tiger, and more -
    http://www.takecontrolbooks.com/tiger-customizing.html
    Subscribe to TidBITS! It's free and smart. http://www.tidbits.com/
  • On Jul 17, 2006, at 7:38 AM, Matt Neuburg wrote:

    > On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen"
    > <stoklund...>:
    >
    >> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    >> object change:(NSDictionary *)change context:(void *)context
    >> {
    >> if ([self respondsToSelector:(SEL)context])
    >> [self performSelector:(SEL)context withObject:change];
    >> }
    >
    > By an incredible coincidence, yesterday (before this reponse
    > arrived at my
    > desk), after considering all the responses I'd gotten, I decided
    > upon an
    > approach and implemented it in my app, and it was word for word,
    > letter for
    > letter identical with this. m.

    I'm just curious ... with the trivial approach above, and the other
    ones involving the context being a keypath and so on ...  has anyone
    considered what will happen if a superclass or subclass (of whatever
    class that is) is also doing KVO (on either same or different keys)
    of the same objects?

    Chris Kane
    Cocoa Frameworks, Apple
  • On Mon, 17 Jul 2006 09:09:04 -0700, Chris Kane <ckane...> said:
    > On Jul 17, 2006, at 7:38 AM, Matt Neuburg wrote:
    >
    >> On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen"
    >> <stoklund...>:
    >>
    >>> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    >>> object change:(NSDictionary *)change context:(void *)context
    >>> {
    >>> if ([self respondsToSelector:(SEL)context])
    >>> [self performSelector:(SEL)context withObject:change];
    >>> }
    >>
    >> By an incredible coincidence, yesterday (before this reponse
    >> arrived at my
    >> desk), after considering all the responses I'd gotten, I decided
    >> upon an
    >> approach and implemented it in my app, and it was word for word,
    >> letter for
    >> letter identical with this. m.
    >
    >
    > I'm just curious ... with the trivial approach above, and the other
    > ones involving the context being a keypath and so on ...  has anyone
    > considered what will happen if a superclass or subclass (of whatever
    > class that is) is also doing KVO (on either same or different keys)
    > of the same objects?

    I hadn't, but I presume you wouldn't be asking the question unless the
    answer were, "Something bad...?" :) I guess you're hinting that one needs to
    call super.

    Still, isn't it the strange implementation of KVO itself that gets us into
    this bind? I mean, we can call super as part of the above implementation,
    but aren't you really saying that we *always* need to call super, or KVO
    will break down? The docs do not mention this (though, to be fair, the
    example in KVOBasics.html does in fact call super). So this is a trap that
    one was always prone to fall into, regardless of how observeValue... was
    implemented.

    The thing I'm really wondering is: Why was KVO implemented in this oddly
    bottleneck-based way? There is terrific prior art, in the form of ordinary
    notifications and the notification center (broadcaster-type architecture);
    is there some technical reason why KVO doesn't use that?

    m.

    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • On 7/17/06, Matt Neuburg <matt...> wrote:
    > On or about 7/17/06 7:57 AM, thus spake "Jakob Olesen" <stoklund...>:
    >
    >> Seriously, is there any reason for checking respondsToSelector?
    >> Wouldn't it be better to get an exception (or crash) if you mistype
    >> the selector or somebody else registers you as an observer?
    >
    > No. That's not how notification (or, for that matter, delegation) behaves.
    > Both those behaviors are considered perfectly acceptable. m.

    Delegation and notifications actually behave completely oppositely in
    this respect. If you fail to implement a delegate method then nothing
    happens except the object uses the default behavior. If you fail to
    implement a notification method (one you explicitly signed up for, not
    one that you get "for free" as part of being a delegate) then you get
    a nice runtime exception when the notification is sent.

    Since this case is much more analogous to notifications than
    delegation, getting the exception is probably the right approach.

    Mike
  • On Jul 17, 2006, at 10:49 AM, Matt Neuburg wrote:
    > On Mon, 17 Jul 2006 09:09:04 -0700, Chris Kane <ckane...> said:
    >> On Jul 17, 2006, at 7:38 AM, Matt Neuburg wrote:
    >>
    >>> On or about 7/17/06 3:38 AM, thus spake "Jakob Olesen"
    >>> <stoklund...>:
    >>>
    >>>> - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    >>>> object change:(NSDictionary *)change context:(void *)context
    >>>> {
    >>>> if ([self respondsToSelector:(SEL)context])
    >>>> [self performSelector:(SEL)context withObject:change];
    >>>> }
    >>>
    >>> By an incredible coincidence, yesterday (before this reponse
    >>> arrived at my
    >>> desk), after considering all the responses I'd gotten, I decided
    >>> upon an
    >>> approach and implemented it in my app, and it was word for word,
    >>> letter for
    >>> letter identical with this. m.
    >>
    >>
    >> I'm just curious ... with the trivial approach above, and the other
    >> ones involving the context being a keypath and so on ...  has anyone
    >> considered what will happen if a superclass or subclass (of whatever
    >> class that is) is also doing KVO (on either same or different keys)
    >> of the same objects?
    >
    > I hadn't, but I presume you wouldn't be asking the question unless the
    > answer were, "Something bad...?" :) I guess you're hinting that one
    > needs to
    > call super.

    No, I wasn't thinking that.  Though having to call super could still
    be a valid conclusion.

    Simply calling super would  be problematic, because NSObject doesn't
    implement it (well, it throws).

    No, I was pondering something more subtle to do with the context.  The
    basic "shunting" pattern here that is somewhat popular is
    fundamentally broken, I think, because people are not considering
    subclassing, and I wonder if the circumlocutions to avoid the problem
    are worth the trouble.

    > The thing I'm really wondering is: Why was KVO implemented in this
    > oddly
    > bottleneck-based way? There is terrific prior art, in the form of
    > ordinary
    > notifications and the notification center (broadcaster-type
    > architecture);
    > is there some technical reason why KVO doesn't use that?

    I'm going to have to leave you hanging on these...

    Chris Kane
    Cocoa Framework, Apple
  • On or about 7/17/06 11:24 AM, thus spake "Chris Kane" <ckane...>:

    > Simply calling super would  be problematic, because NSObject doesn't
    > implement it (well, it throws).

    True. But that is what the example does here:

    <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueObserving
    /Concepts/KVOBasics.html#//apple_ref/doc/uid/20002252-179866
    >

    (Interestingly, the comment in that example is completely different from the
    comment in the copy on my machine.) The code shown in the example will break
    if super is NSObject. So perhaps I should file a bug on the docs.

    >> The thing I'm really wondering is: Why was KVO implemented in this oddly
    >> bottleneck-based way? There is terrific prior art, in the form of ordinary
    >> notifications and the notification center (broadcaster-type architecture); is
    >> there some technical reason why KVO doesn't use that?
    >
    > I'm going to have to leave you hanging on these...

    Ooooh, I think I like the sound of that; maybe I won't need to file a bug
    after all! :) m.

    --
    matt neuburg, phd = <matt...>, http://www.tidbits.com/matt/
    pantes anthropoi tou eidenai oregontai phusei
    AppleScript: the Definitive Guide - Second Edition!
    http://www.amazon.com/gp/product/0596102119
    Take Control of Word 2004, Tiger, and more -
    http://www.takecontrolbooks.com/tiger-customizing.html
    Subscribe to TidBITS! It's free and smart. http://www.tidbits.com/
  • On Jul 17, 2006, at 2:31 PM, Matt Neuburg wrote:

    > KeyValueObserving
    > /Concepts/KVOBasics.html#//apple_ref/doc/uid/20002252-179866>
    >
    > (Interestingly, the comment in that example is completely different
    > from the
    > comment in the copy on my machine.) The code shown in the example
    > will break
    > if super is NSObject. So perhaps I should file a bug on the docs.

    Actually, that was why it says "if it implements it".  Now I suppose
    one could argue that NSObject does implement it, but as Chris said,
    it just throws.
  • On Jul 17, 2006, at 1:00 PM, Scott Anguish wrote:
    > On Jul 17, 2006, at 2:31 PM, Matt Neuburg wrote:
    >> <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueObserving

    >> /Concepts/KVOBasics.html#//apple_ref/doc/uid/20002252-179866>
    >>
    >> (Interestingly, the comment in that example is completely different
    >> from the
    >> comment in the copy on my machine.) The code shown in the example
    >> will break
    >> if super is NSObject. So perhaps I should file a bug on the docs.
    >
    > Actually, that was why it says "if it implements it".  Now I
    > suppose one could argue that NSObject does implement it, but as
    > Chris said, it just throws.

    The problem with that would be that "if it implements it" can change
    over time.  Next month you change a superclass of the class to start
    listening for KVO notifications (perhaps you install a software update
    of the OS!), and ... your hard-coded knowledge of what the super class
    did and didn't do is no longer true.

    You can't just blindly call super, and face the exception (hmm, is the
    exception there for a reason? could be a clue), but if you don't you
    cut off your superclasses from receiving necessary KVO notifications,
    given the design.

    Nobody's figured out the tricky problem yet ....

    [I didn't design KVO, I'm just a bystander.]

    Chris Kane
    Cocoa Frameworks, Apple
  • On 17/07/2006, at 18.09, Chris Kane wrote:

    > I'm just curious ... with the trivial approach above, and the other
    > ones involving the context being a keypath and so on ...  has
    > anyone considered what will happen if a superclass or subclass (of
    > whatever class that is) is also doing KVO (on either same or
    > different keys) of the same objects?

    Well, not until you brought it up.

    If you are subclassing a foreign class without documented behavior,
    the context pointer is useless. Basically you don't know if the
    superclass is already observing an object, so the context pointer
    could be mine or the superclass', who knows?

    Further, if you are expecting to be subclassed, you must document
    exactly which objects and key paths you are observing, otherwise the
    subclass can accidentally overwrite your context pointer. (What
    happens if you call addObserver twice on the same object/key path?)

    Quick test: you get two callbacks, calling removeObserver removes
    them one at a time, last in first out. (linked list?)
    Calling addObserver twice with identical arguments (incl. context)
    also gives you two callbacks.

    So, if you don't trust your subclasses and superclasses, you are
    given a void pointer that you may or may not have created yourself.
    That is very close to useless.

    This is what I would do, if I was writing class intended for
    subclassing: Use

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
        [self performSelector:(SEL)context withObject:[NSNotification
    notificationWithName:keyPath object:object userInfo:change]];
    }

    and tell subclasses that they can depend on it being so. Tell
    subclasses to not call super with a context pointer that is not a
    valid selector. Know that one change may cause multiple callbacks.

    When writing a subclass: Ignore context completely, dispatch on
    keyPath and/or object

    Alternative solution: Create your own, private helper object and
    register that instead.

    @interface KVOHelper {
        id owner;
    }
    @end

    @implementation KVOHelper
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
        [owner performSelector:(SEL)context withObject:[NSNotification
    notificationWithName:keyPath object:object userInfo:change]];
    }
    @end

    Don't tell anyone about that object, and you'll be fine.

    The KVO API expects observers to have a single personality, so that
    is probably the way to go if you are in the middle of a class hierarchy.
  • On or about 7/17/06 1:42 PM, thus spake "Chris Kane" <ckane...>:

    > On Jul 17, 2006, at 1:00 PM, Scott Anguish wrote:
    >> On Jul 17, 2006, at 2:31 PM, Matt Neuburg wrote:
    >>> <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueObserving

    >>> /Concepts/KVOBasics.html#//apple_ref/doc/uid/20002252-179866>
    >>>
    >>> (Interestingly, the comment in that example is completely different
    >>> from the
    >>> comment in the copy on my machine.) The code shown in the example
    >>> will break
    >>> if super is NSObject. So perhaps I should file a bug on the docs.
    >>
    >> Actually, that was why it says "if it implements it".  Now I
    >> suppose one could argue that NSObject does implement it, but as
    >> Chris said, it just throws.
    >
    > The problem with that would be that "if it implements it" can change
    > over time.  Next month you change a superclass of the class to start
    > listening for KVO notifications (perhaps you install a software update
    > of the OS!), and ... your hard-coded knowledge of what the super class
    > did and didn't do is no longer true.
    >
    > You can't just blindly call super, and face the exception (hmm, is the
    > exception there for a reason? could be a clue), but if you don't you
    > cut off your superclasses from receiving necessary KVO notifications,
    > given the design.

    And there's a memory management problem. The "context" is not automatically
    retained. Therefore if ClassA provided a context, only ClassA knows whether
    that context needs to be released (or what it means). Gosh, this looks
    troublesome on *any* implementation.

    So ClassB needs a way to distinguish the notifications that belong to it (so
    that it can pass along those that don't). The only way I can think of to do
    this is to include an unmistakeable token in the context. But what would
    this be - a hard-coded string? Yuck. m.

    --
    matt neuburg, phd = <matt...>, http://www.tidbits.com/matt/
    pantes anthropoi tou eidenai oregontai phusei
    AppleScript: the Definitive Guide - Second Edition!
    http://www.amazon.com/gp/product/0596102119
    Take Control of Word 2004, Tiger, and more -
    http://www.takecontrolbooks.com/tiger-customizing.html
    Subscribe to TidBITS! It's free and smart. http://www.tidbits.com/
  • On Jul 17, 2006, at 2:16 PM, Jakob Olesen wrote:
    > On 17/07/2006, at 18.09, Chris Kane wrote:
    >
    >> I'm just curious ... with the trivial approach above, and the other
    >> ones involving the context being a keypath and so on ...  has
    >> anyone considered what will happen if a superclass or subclass (of
    >> whatever class that is) is also doing KVO (on either same or
    >> different keys) of the same objects?
    >
    > Well, not until you brought it up.
    >
    > If you are subclassing a foreign class without documented behavior,
    > the context pointer is useless. Basically you don't know if the
    > superclass is already observing an object, so the context pointer
    > could be mine or the superclass', who knows?
    >
    > Further, if you are expecting to be subclassed, you must document
    > exactly which objects and key paths you are observing, otherwise the
    > subclass can accidentally overwrite your context pointer. (What
    > happens if you call addObserver twice on the same object/key path?)
    >
    > Quick test: you get two callbacks, calling removeObserver removes
    > them one at a time, last in first out. (linked list?)
    > Calling addObserver twice with identical arguments (incl. context)
    > also gives you two callbacks.
    >
    > So, if you don't trust your subclasses and superclasses, you are
    > given a void pointer that you may or may not have created yourself.
    > That is very close to useless.
    >
    > This is what I would do, if I was writing class intended for
    > subclassing: Use
    >
    > - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    > object change:(NSDictionary *)change context:(void *)context
    > {
    > [self performSelector:(SEL)context withObject:[NSNotification
    > notificationWithName:keyPath object:object userInfo:change]];
    > }
    >
    > and tell subclasses that they can depend on it being so. Tell
    > subclasses to not call super with a context pointer that is not a
    > valid selector. Know that one change may cause multiple callbacks.
    >
    > When writing a subclass: Ignore context completely, dispatch on
    > keyPath and/or object

    You and Matt Neuburg and Jim Correia (privately) have all now
    identified all the parts of the problem and the solution.  But I don't
    like the "sub/superclasses can't use whatever context they like" that
    results from your "document the behavior and requirements" approach.
    So I'll present a slightly different solution.

    When you and a superclass both register for KVO notifications, the
    registrations are necessarily distinct, and you will potentially
    receive multiple calls to observeValueForKeyPath:... for the same
    changes, some due to your superclass, some due to your registrations.
    The ones for you you need to process and not pass up to super.  The
    ones not for you you need to pass up to super and not process (that
    is, not use or expect anything about the context).  You cannot look at
    the keypath, recognize it, and say "this is for me" and consume it,
    because the keypath may be interesting to the superclass too, but you
    can't pass all of those along either, because the superclass may not
    be interested (and is certainly not interested in your context if this
    invocation happens to contain your context).  Similarly keypath+object
    is not reliable to distinguish "mine" from "somebody else's".

    The solution is that the context pointer must be used to provide a
    globally unique value that you can recognize in your class.  If you
    recognize the value, this notification is for you, otherwise you pass
    the method call up to super and return.  You have to use a value that
    the other classes in the hierarchy won't (or can't) use.

    You cannot use NULL as the context pointer, because the superclass (or
    a subclass) might also use NULL.  NULL is a shared value.  You can't
    use selectors, either, because their values are global to the process,
    and a sub or superclass could potentially use the same one.  Plus,
    recognizing many possible selector values would be a pain and time
    consuming.  It's better to pick one context value.

    Matt Neuburg said:
    > So ClassB needs a way to distinguish the notifications that belong
    > to it (so
    > that it can pass along those that don't). The only way I can think
    > of to do
    > this is to include an unmistakeable token in the context. But what
    > would
    > this be - a hard-coded string? Yuck. m.

    You can't include something *inside* the context, because you can't
    safely look through the pointer at anything in particular, as the
    pointer might not be pointing to the data structure you know about.
    You have to generate a unique pointer value and act based on that.  An
    == comparison is dirt cheap if you can do it.

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)obj
    change:(NSDictionary *)change context:(void *)ctx {
        if (ThisClassUniqueObservationContext != ctx) {
            [super observeValueForKeyPath:keyPath ofObject:obj
    change:change context:ctx];
            return;
        }
        if ([keyPath isEqual:@"isBrillig"]) {
    ... and so on ...
        }
        ... and so on ...
    }

    If you assume that @"" string constants are NOT globally uniqued in a
    binary, but only to a given compilation unit, then
    ThisClassUniqueObservationContext could be a constant string:

    static NSString * ThisClassUniqueObservationContext = @"MyContext";

    That's not necessarily a safe assumption, however.  The following is
    certainly bad, since CStrings are uniqued and shared within a binary:

    static const char * ThisClassUniqueObservationContext = "MyContext";

    What *I* have done when I needed to deal with this is create some
    useless but unique data and use its address:

    static char __MyContext = 0;
    static char *ThisClassUniqueObservationContext = &__MyContext;

    Note that if this were "const", that could thwart things, since
    integer constants might also be uniqued and shared by the linker.

    What could still happen is that the compiler could notice that this
    data is static (local to this compilation unit) and never stored to
    anywhere in the compilation unit and nothing ever writes through the
    address to it (or ThisClassUniqueObservationContext) or passes the
    address to a function or whatever, and decide in an optimizing kind of
    way to make the data const for me, and possibly subject it to linker
    uniquing.  But that possibility doesn't keep me up at night.

    But whatever way you dice it, "yuck".  Usually I've not wanted the
    context pointer for anything and just wanted to use NULL ... but for
    this potential problem.

    > Alternative solution: Create your own, private helper object and
    > register that instead.
    > [...]
    > The KVO API expects observers to have a single personality, so that
    > is probably the way to go if you are in the middle of a class
    > hierarchy.

    I've done that solution too.  There you've made the observer different
    rather than the context different.  I've also used that to work around
    "can't register self for notifications about self" issue, which should
    someday be fixed.  Once an object can register itself to receive
    notifications about itself, this class/superclass/subclass collision
    issue will probably become more common in practice than it is now.

    Chris Kane
    Cocoa Frameworks, Apple
  • On Jul 17, 2006, at 9:00 PM, Chris Kane wrote:

    > The solution is that the context pointer must be used to provide a
    > globally unique value that you can recognize in your class.  If you
    > recognize the value, this notification is for you, otherwise you
    > pass the method call up to super and return.  You have to use a
    > value that the other classes in the hierarchy won't (or can't) use.
    >
    > You cannot use NULL as the context pointer, because the superclass
    > (or a subclass) might also use NULL.  NULL is a shared value.  You
    > can't use selectors, either, because their values are global to the
    > process, and a sub or superclass could potentially use the same
    > one.  Plus, recognizing many possible selector values would be a
    > pain and time consuming.  It's better to pick one context value.

    So one potential solution here is to allocate a dictionary, use that
    as the context for your class, and also use it as a dispatch table.

    Since the dictionary is dynamically allocated using it as the void *
    context will guarantee uniqueness in the same way as Chris' "ugly"
    solution in so far as all addresses within the address space are
    unique. It won't guarantee uniqueness if anyone else uses arbitrary
    context pointers like this:

    static void *ObservationContext = (void *)2091;

    The observer would look something like this:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
    if (context == dispatchDictionary) {
      SEL selector = NSSelectorFromString([dispatchDictionary
    objectForKey: keyPath]);
      // dispatch to self based on the selector looked up in the table
    } else {
      [super observeValueForKeyPath: keyPath ofObject: object change:
    change context: context];
    }
    }

    While this solves the problem Matt wanted to solve, there is still a
    fair bit of ugliness and hand waving going on.

    You have to make sure you have one dispatch table per class. You have
    to keep the dispatch table up to date (as observers are registered).
    Your class has to know about the dispatch table and correctly
    implement -observeValueForKeyPath:ofObject:change:context:. It is
    left as an exercise for the reader to accomplish this correctly,
    without cut and paste programming.

    If I've glossed over something (besides the intentional hand waving),
    hopefully someone will point it out.

    Jim
  • On 18/07/2006, at 3.00, Chris Kane wrote:

    > You and Matt Neuburg and Jim Correia (privately) have all now
    > identified all the parts of the problem and the solution.

    Well, not really. Note that removeObserver:forKeyPath: does not take
    a context argument. It just removes the last context added. This
    means it is not safe to remove yourself as an observer until dealloc
    (or didTurnIntoFault). You also need to trust that sub- and
    superclasses will do the same.

    Otherwise you have to use a helper object.

    > The solution is that the context pointer must be used to provide a
    > globally unique value that you can recognize in your class.  If you
    > recognize the value, this notification is for you, otherwise you
    > pass the method call up to super and return.  You have to use a
    > value that the other classes in the hierarchy won't (or can't) use.

    This is of course the way to go, but in the process we have
    unanswered Matt's original question, "How are you guys doing the
    dispatch thing?"

    ...and it is really painful to waste a perfectly good void pointer on
    multiple personality resolution. :-)

    How about this:

    static SEL dispatch[10]; // make it big enough or die violently

    static void* sel2ctx(SEL s)
    {
        int i;
        for (i=0; i<sizeof(dispatch)/sizeof(dispatch[0]); i++) {
            if (!dispatch[i]) dispatch[i]=s;
            if (s==dispatch[i]) return dispatch+i;
        }
        abort(); // I told you...
    }

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
    object change:(NSDictionary *)change context:(void *)context
    {
        SEL *entry = (SEL*)context;
        if (entry>=dispatch && entry<dispatch+sizeof(dispatch)/sizeof
    (dispatch[0])) {
            [self performSelector:*entry withObject:...];
        }
        else {
            [super observeValueForKeyPath:keyPath ofObject:object
    change:change context:context];
        }
    }

    fgrep -c @selector MyClass.m would be an upper bound on the necessary
    size of the dispatch table
    Oh, and you would have to pre-fill the table if you are
    multithreaded. Call sel2ctx in +initialize or use an initializer list:

    sed -n 's/^.*\(@selector([^)]*)\).*$/    \1,/p' MyClass.m | sort -u

    This is not just a matter of performance, also convenience. You
    already have to keep your addObserver and removeObserver calls in
    sync. Adding custom dispatch code to observeValueForKeyPath makes it
    even harder to maintain.

    Also, keyPath+object is not always so great for deciding what to do.
    Imagine observing the same keyPath on objects in two collections. You
    have to check the collections. Now imagine the same object present in
    both collections. You get two identical callbacks.

    So what is the right thing to do? It depends.

    For a heavy-weight class (few instances, lots of state) use a helper
    object.
    If you need removeObserver outside dealloc/didTurnIntoFault, use a
    helper object.
    If you need to observe self, use a helper object.
    For a light-weight class (lots of instances, little state) use a
    unique context and custom dispatch if it can be kept simple,
    otherwise a dispatch table.
    If you are working alone and writing a non-reusable NSObject
    subclass, just use a selector directly. (Don't complicate things
    until you have to).

    > You can't include something *inside* the context, because you can't
    > safely look through the pointer at anything in particular, as the
    > pointer might not be pointing to the data structure you know
    > about.  You have to generate a unique pointer value and act based
    > on that.  An == comparison is dirt cheap if you can do it.

    I have seen Windows code ask the virtual memory manager if an address
    is readable, then check for a magic value. Messy and not 100% safe,
    but possible.

    > If you assume that @"" string constants are NOT globally uniqued in
    > a binary, but only to a given compilation unit, then
    > ThisClassUniqueObservationContext could be a constant string:
    >
    > static NSString * ThisClassUniqueObservationContext = @"MyContext";

    CFSTR() does global uniqueing, at least in the open-sourced version,
    but there is also a mystery __builtin___CFStringMakeConstantString
    implementation.

    > Note that if this were "const", that could thwart things, since
    > integer constants might also be uniqued and shared by the linker.

    Really? I thought only C++ allowed that. C99? After all you are
    passing a pointer to your constant outside the compilation unit.

    > But whatever way you dice it, "yuck".  Usually I've not wanted the
    > context pointer for anything and just wanted to use NULL ... but
    > for this potential problem.

    I agree.

    NSNotificationCenter causes less trouble by taking a selector instead
    of a void pointer. It still has the problem with removing observers,
    though. removeObserver:name:object: does not take a selector
    argument, so it is the same mess if you try to unregister before
    dealloc.
  • On Jul 17, 2006, at 9:00 PM, Chris Kane wrote:

    > If you assume that @"" string constants are NOT globally uniqued in
    > a binary, but only to a given compilation unit, then
    > ThisClassUniqueObservationContext could be a constant string:
    >
    > static NSString * ThisClassUniqueObservationContext = @"MyContext";

    Even if the strings are globally uniqued across the entire binary,
    assuming a FBFroobazle class, something like this is probably good
    enough:

    static NSString * FBFroobazzleValueObservationContext =
    @"FBFroobazzleValueObservationContext";

    Certainly better than

    static void * FBFroobazzleValueObservationContext = (void *)2192;

    There is the chance that someone else will use the string
    @"FBFroobazzleValueObservationContext" as an observation context
    elsewhere in the inheritance tree, but I think that a) it would be a
    programmer error b) is probably not worth losing any sleep over.

    And there is certainly a smaller chance of a collision than with an
    arbitrarily picked number.

    Jim
  • > FROM : George Orthwein
    > DATE : Sat Jul 15 07:36:10 2006
    >
    > For a custom view that responds to a few dozen attributes of the
    > controller's current selection....
    >
    > - (void)observeValueForKeyPath:(NSString *)keyPath
    > ofObject:(id)object
    > change:(NSDictionary *)change
    > context:(void *)context
    > {
    > NSString *keyPathName;
    > if ([keyPath isEqualToString:@"selection.bgImage.color"] == YES){
    > // handle special case of this attribute of a relationship
    > keyPathName = @"bgImageColor";
    > } else {
    > // convert "selection.attribute" keypath to appropriate accessor
    > name -- by abusing pathExtension method :)
    > keyPathName = [keyPath pathExtension];
    > }
    > [self setValue:[object valueForKeyPath:keyPath] forKey:keyPathName];
    > }
    >
    > So observing "selection.attribute" will call my setAttribute accessor
    > with the new value via KVC.

    Interestingly, though (or perhaps not, for those who already realized this),
    if what you are planning to do when you get notified thru observeValue... is
    set a corresponding ivar, it might be much simpler to avoid the entire
    problem and use bind:... instead of addObserver:... at the outset.

    This goes back to my earlier thread, "two-way bindings and one-way
    bindings":

    <http://www.cocoabuilder.com/archive/message/cocoa/2006/7/6/166949>

    The point there was that if I say bind:..., I get a "one-way" binding, which
    in effect means that I start observing the thing I'm bound to. In that
    thread I was concentrating on the downside, which is that the thing I'm
    bound to does not automatically also start observing me; but now let's talk
    about the upside. The upside is that my ivar gets set automatically when the
    other object's observed attribute changes - without ever passing through
    observeValue...!

    So, if what you were going to do was call setValue:forKey: on your own ivar,
    you could skip the whole addObserver: thing and just use bind:, because
    that's exactly what then *will* happen; setValue:forKey: will be called on
    your ivar.

    Moreover, if you wanted to do anything else when this happens, you can
    implement the accessors to your ivar and do it there.

    Moreover, in the case of something like an NSController's "content", this
    approach works, when ordinary observing with addObserver: does not. As has
    been pointed out several times recently, the change dictionary passed to
    observeValue... is empty. But if you start with bind:, it works great; the
    new value just gets handed to you directly.

    Thus, taking advantage of the aspect of bind:... that you get "for free"
    could constitute an economical workaround to the observeValueForKeyPath
    bottleneck. Of course this is officially a misuse of bindings, but on the
    other hand we've already agreed that the observeValueForKeyPath bottleneck
    architecture is wretched, so one may be forgiven for thinking, "any port in
    a storm..." m.

    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • On Jul 18, 2006, at 8:44 PM, Matt Neuburg wrote:
    > Interestingly, though (or perhaps not, for those who already
    > realized this),
    > if what you are planning to do when you get notified thru
    > observeValue... is
    > set a corresponding ivar, it might be much simpler to avoid the entire
    > problem and use bind:... instead of addObserver:... at the outset.

    Thanks for this idea, though I actually arrived at my current
    observeValueForKeyPath: implementation because I had initially using
    bindings! :)
    http://www.cocoabuilder.com/archive/message/cocoa/2006/4/17/161225

    I already had all the setters there that were called with
    bind:toObject:withKeyPath:options: so when I switched to KVO, it
    naturally led into trying to call them from my
    observeValueForKeyPath: method.

    One nice thing is that I can switch back and forth easily just by
    swapping out:
    [sender bind:@"attributeName" toObject:docPrefsController
    withKeyPath:@"selection.attributeName" options:nil];
    for
    [docPrefsController addObserver:sender
    forKeyPath:@"selection.attributeName" options:nil context:NULL];
    As you point out, one passes through observeValueForKeyPath: and one
    calls the accessors directly.

    The main reason I switched away from using bind: was that I found my
    setters getting called something like a dozen times when the binding
    was first initiated with bind:toObject:withKeyPath:options:. Perhaps
    there's a way to coalesce those initial updates. I found it was
    bogging down the initial display of the view since it triggered a
    dozen or so redraws.
    (Ok, I just double checked this and I'm only seeing a single initial
    call now when I use bind:! Not sure what I was seeing before because
    in my notes I even mention some accessors getting called 24 times! I
    probably setup something wrong at some point, though I thought I had
    also seen this mentioned in the archives somewhere. More
    investigation is in order....)

    One thing that tripped me up when I switched to KVO is that
    addObserver:forKeyPath:options:context: does NOT send the current
    value on initial registration. This means that to sync the view
    initially I have to enumerate through the relevant attributes and
    call setValue:forKey: manually.
    http://www.cocoabuilder.com/archive/message/cocoa/2006/5/20/163920

    > The point there was that if I say bind:..., I get a "one-way"
    > binding, which
    > in effect means that I start observing the thing I'm bound to. In that
    > thread I was concentrating on the downside, which is that the thing
    > I'm
    > bound to does not automatically also start observing me; but now
    > let's talk
    > about the upside. The upside is that my ivar gets set automatically
    > when the
    > other object's observed attribute changes - without ever passing
    > through
    > observeValue...!
    >
    > So, if what you were going to do was call setValue:forKey: on your
    > own ivar,
    > you could skip the whole addObserver: thing and just use bind:,
    > because
    > that's exactly what then *will* happen; setValue:forKey: will be
    > called on
    > your ivar.

    True. I guess if you want to avoid the KVO "bottleneck" you can just
    use "one way" bindings instead!

    > Moreover, in the case of something like an NSController's
    > "content", this
    > approach works, when ordinary observing with addObserver: does not.
    > As has
    > been pointed out several times recently, the change dictionary
    > passed to
    > observeValue... is empty. But if you start with bind:, it works
    > great; the
    > new value just gets handed to you directly.

    Is this right?. If you bind to "arrangedObjects" the entire array is
    sent each time it is modified. I tried binding to "content" and
    wasn't getting any updates on modifications so maybe my test was
    wrong anyhow. But it doesn't look like you can get just the change/
    removed values using bind.

    > Of course this is officially a misuse of bindings,

    I don't think implementing "one way" or "read only" bindings is a
    misuse, though it almost seems that way since there aren't any
    examples or mentions of it in the docs that I remember seeing. I
    think I'll file a doc request.
    It does seem to be a sly way of avoiding the observeValueForKeyPath
    bottleneck though.

    I think I better add a disclaimer since many details of this whole
    thing are still fuzzy in my head: Anything stated above may be
    completely wrong. There, I feel better. ;)

    George
  • On Jul 20, 2006, at 3:25 PM, George Orthwein wrote:

    > I don't think implementing "one way" or "read only" bindings is a
    > misuse, though it almost seems that way since there aren't any
    > examples or mentions of it in the docs that I remember seeing. I
    > think I'll file a doc request.

    binding between model objects isn't recommended at all. the bindings
    doc is clear about how we recommend doing it (controller to model,
    view to controller.. it would be useful to extend that to the
    occasional view to model as well though, I'll add it to the list).
    We don't say how we don't recommend doing it in general though..

    > It does seem to be a sly way of avoiding the observeValueForKeyPath
    > bottleneck though.
  • On Thu, 20 Jul 2006 16:14:51 -0400, Scott Anguish <scott...> said:
    >
    > On Jul 20, 2006, at 3:25 PM, George Orthwein wrote:
    >
    >> I don't think implementing "one way" or "read only" bindings is a
    >> misuse, though it almost seems that way since there aren't any
    >> examples or mentions of it in the docs that I remember seeing. I
    >> think I'll file a doc request.
    >
    > binding between model objects isn't recommended at all. the bindings
    > doc is clear about how we recommend doing it (controller to model,
    > view to controller.. it would be useful to extend that to the
    > occasional view to model as well though, I'll add it to the list).

    However, those terms don't carry all that much meaning in real life. They
    are useful as conceptual notions of the role a class is to play in the app,
    but what exactly each of those things *is* becomes very muddy in actual
    practice.

    For example, I'm using bind: to cause my main app controller, MyObject, to
    have its "paths" ivar synced with a "paths" value supplied (thru manual KVO)
    by a matrix subclass, MyMatrix. Would you try to claim that this is wrong?
    Why? On the grounds that MyMatrix isn't a view? (It's a NSMatrix! It's
    sitting right in a window!!) Would you try to claim that MyObject isn't a
    controller? (Its job is exactly to coordinate the mediation between data and
    interface; if that isn't a controller, what is?) And yet formally there
    isn't the slightest difference between this and using bind: to cause an
    arbitrary ClassA to observe an ivar in an arbitrary ClassB.

    Or, let's look it another way: as I pointed out in an earlier thread, when
    you say bind: to bind MyObject to the NSUserDefaultsController, nothing
    special happens; NSUserDefaultsController isn't any more bind-savvy than
    MyObject, and the binding is still one-way only (it's just the observing
    part, in one direction). Yet NSUserDefaultController is an NSController
    subclass and has "controller" in its name. In fact, I've even done a
    double-bind, binding from MyObject to NSUserDefaultController, and from
    NSUserDefaultController's "values" back to MyObject, using the same key, to
    create a true two-way binding so that the keys are in constant sync in both,
    and nothing bad has happened. In fact, what happened is good! It's a
    binding!

    So it seems to me wrong on the facts and wrong on the law to say you can't
    use bind: like this. It's wrong on the facts because it works great, whereas
    the addObserver: / observeKeyValue... thing (as posters on this thread and
    others have already pointed out) doesn't work particularly well. It's wrong
    on the law because it is plainly *meant* to work, creating a nice
    automatically maintained pairing between a key in one object and a key in
    another object, whereas the addObserver: / observeKeyValue... thing is an
    architectural nightmare for your code.

    (Anyway, you don't say not to use the addObserver: / observeKeyValue...
    thing between two model objects. Yet I fail to see the difference between
    this and bind: - the effect is just the same, except that bind: works
    better.)

    In short, I have come to see the warning against bind: as a kind of canard;
    it is a cute but purely theoretical grumble. In actual practice, I am now
    using bind: like crazy and my code has been wonderfully streamlined. Once
    we're past awakeFromNib, where all the bind: commands are given (wherever a
    binding could not be set up in the nib, of course), MyObject becomes
    self-contained; it finds within its own ivars all the values of all the bits
    of the interface, and it can change any of those bits by setting one of
    those ivars. No outlets to the view, no consultation of the view as if it
    contained data (i.e. as if it were a model). Isn't that exactly what
    bindings are for??? m.

    --
    matt neuburg, phd = <matt...>, <http://www.tidbits.com/matt/>
    A fool + a tool + an autorelease pool = cool!
    AppleScript: the Definitive Guide - Second Edition!
    <http://www.amazon.com/gp/product/0596102119>
  • in my original reply, when I said controller I meant specifically
    NS*Controller
    On Jul 20, 2006, at 9:57 PM, Matt Neuburg wrote:

    > For example, I'm using bind: to cause my main app controller,
    > MyObject, to
    > have its "paths" ivar synced with a "paths" value supplied (thru
    > manual KVO)
    > by a matrix subclass, MyMatrix. Would you try to claim that this is
    > wrong?

    as much as I can tell here, yes.

    The recommendation is that bindings should not be used between two
    model objects. Binding views directly to model objects should only be
    done (and even then it isn't something that is currently suggested)
    when there is no updating of the values due to changes in the U
    (essentially read-only).

    > In short, I have come to see the warning against bind: as a kind of
    > canard;
    > it is a cute but purely theoretical grumble.

    We've talked at length with engineering about it, and it isn't
    something that we suggest doing.

    > Isn't that exactly what
    > bindings are for??? m.
    >
    bindings are intended to eliminate glue code between views and model
    objects

    KVO is the better solution.  if the observeValeForKeyPath: method is
    a bottleneck or results in ugly code it is quite possible to
    encapsulate that observing code into an object...
  • On Jul 21, 2006, at 4:11 AM, Scott Anguish wrote:
    > in my original reply, when I said controller I meant specifically
    > NS*Controller

    So the only recommended application of "one way" bindings would be
    between an NS*Controller and an NSView subclass, or also possibly
    between an NS*Controller and a model class?

    That's good to know and I don't think it's come up in past
    discussions, or at least stated as plainly. The lack of any mention
    of "one way" bindings is what I was referring to in the docs...
    though it now seems that there is only a specific application for
    them anyway. (Well, NS*Controller and a custom view should be pretty
    common....)

    It's a good point for this discussion though, since we seemed to be
    settling in on:

    > True. I guess if you want to avoid the KVO "bottleneck" you can
    > just use "one way" bindings instead!

    A general statement like doesn't work, since KVO can be used in
    situations where "one way" bindings aren't recommended (Matt's
    position notwithstanding :) ).

    Thanks,
    George
  • On Jul 21, 2006, at 11:46 AM, George Orthwein wrote:

    > On Jul 21, 2006, at 4:11 AM, Scott Anguish wrote:
    >> in my original reply, when I said controller I meant specifically
    >> NS*Controller
    >
    > So the only recommended application of "one way" bindings would be
    > between an NS*Controller and an NSView subclass, or also possibly
    > between an NS*Controller and a model class?

    I'm not sure what you mean by "one-way".  We have what are called
    "read-only" bindings (where the attribute that you've bound to an
    object will not cause any changes in the UI to be propagated back to
    the object).

    >
    > That's good to know and I don't think it's come up in past
    > discussions, or at least stated as plainly. The lack of any mention
    > of "one way" bindings is what I was referring to in the docs...
    > though it now seems that there is only a specific application for
    > them anyway. (Well, NS*Controller and a custom view should be
    > pretty common....)
    >
    > It's a good point for this discussion though, since we seemed to be
    > settling in on:
    >
    >> True. I guess if you want to avoid the KVO "bottleneck" you can
    >> just use "one way" bindings instead!

    just to ensure clarity. this is not my position and I didn't write
    that.

    >
    > A general statement like doesn't work, since KVO can be used in
    > situations where "one way" bindings aren't recommended (Matt's
    > position notwithstanding :) ).

    Regardless of matt's position, it isn't recommended.
previous month july 2006 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