Re: basic bindings question
Re: basic bindings question
- Subject: Re: basic bindings question
- From: Ken Thomases <email@hidden>
- Date: Wed, 14 May 2008 16:11:08 -0500
On May 14, 2008, at 2:08 PM, Daniel Child wrote:
All I'm trying to do is to replace the target-action-outlet
approach with bindings in the simplest case I can think of: a text
field whose value would correspond to an ivar in the controller.
One text field would receive the number, a button would click, pass
the number to the ivar, and redisplay that value in another text
field.
Actually, requiring the button click to perform the transfer is going
to make it harder. It will be easier to just have the field directly
update the ivar when editing completes.
First off, is it possible possible to bind directly to an instance
variable in the controller? In my hyper-basic example, I'm
collapsing M and C. I thought doing so should be OK, so I tried:
Bind to: Controller
Model Key Path: self.number (number is the ivar) (self presumably
being the controller)
This didn't work.
Leave out the "self.". Just use "number".
Unfortunately, I am also confused about the intended receiver for
addObserver: forKeyPath:. I thought it would be the ivar (number),
but I get a warning that NSNumber may not respond to that method
(and sure enough the docs show that it doesn't). Logically, the
second text field should "observe" that the model/controller ivar
(number) has changed. But I thought you were not supposed to use
outlets either.
There are two common misconceptions here:
1) KVO is not for observing properties, as such. It's for observing
the object which _has_ the property. To put it another way: I don't
observe the number, I observe the controller for changes in its
number property.
2) A property is _not_ the ivar. The ivar, if it exists at all, is
an implementation detail of how the class implements the property. A
property is part of the interface of the object. You will find it
much easier if you conceptualize a property as the set of KVC-
conforming methods in the object's interface. A "key" is a string
naming or identifying a property. (Yes, KVC and KVO provide built-in
support for properties which don't have accessor methods. They will
access the ivar directly. However, this is just a convenience and a
fall-back position. It doesn't materially change how you should
conceptualize properties.)
To illustrate:
@interface Foo : NSObject
{
NSNumber* number; // <-- This is NOT the property
}
// _These_ are the property:
- (NSNumber*) number;
- (void) setNumber:(NSNumber*)newNumber;
@end
// ... in some other code somewhere
Foo* myFoo = /* ... */
[myFoo addObserver:self forKeyPath:@"number"];
This object (self) is observing the myFoo object for changes in its
"number" property.
Think also about the KVO notifications you will receive:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context;
Think about what you will be receiving in the "object" parameter of
that. You're not interested in knowing that some anonymous, free-
floating NSNumber object has changed; you need to be informed which
object has had its "number" property changed. So, in this case, it
would be the Foo object which was myFoo in the above snippet. That's
why you need to conceptualize KVO as observing the object which has
the property, not as observing the property (or ivar) itself.
Consider the possibility that I use a plain "int" instead of an
NSNumber as the backing store for the number property of a Foo.
Surely, the "object" parameter of the observeValueForKeyPath...
method can't be that int, because an int isn't an object.
Furthermore, consider if there is _no_ backing storage for the number
property of a Foo. What if I changed the above @interface to omit
the "number" ivar. Foo still has a "number" property. Its meaning/
value is whatever results from the semantics of the two methods -
number and -setNumber:. In theory, they could compute the value on
the fly, call some library API, or even query a remote server to
determine the value. They could do anything and it need not involve
an ivar of any sort.
Lastly, I want to address a confusion that I've seen at other times
on this list (but which you didn't express). Often people have a
class with an NSMutableArray ivar, and they get confused as to why
modifications that they make directly to that ivar (as by -
addObject:, for example) don't result in KVO notifications and
updates to the bound GUI elements. The reason is that nothing is
observing that array. They are observing the owning object for
changes of the property for which that ivar is backing storage.
The array does not (and can not) send out KVO notifications. For one
thing, it doesn't know what object owns it nor what property it
represents. The owning object is what sends out the KVO
notifications. In order for it to do that, the owning object must be
messaged. It might be messaged with a KVC-conforming setter such as
set<Key>: or insertObject:in<Key>AtIndex:, in which case KVO hooks
into those methods using low-level techniques of the Objective-C
runtime (isa-swizzling). Or, it might be messaged with will/
didChange:valuesAtIndexes:forKey:. One way or another, though, the
owning object has to be messaged if it is to produce KVO notifications.
KVO does provide the -mutableArrayValueForKey... methods to create a
proxy object which you can treat as a mutable array but which
messages the owning object to carry out the actual modifications. In
that way, the owning object is able to send out KVO notifications as
necessary. (Even if the proxy ends up accessing the ivar directly,
it still messages the owning object with -will/
didChange:valuesAtIndexes:forKey:.)
I hope that helps clarify.
Cheers,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden