Re: Bindings - registering change notification for multiple keys
Re: Bindings - registering change notification for multiple keys
- Subject: Re: Bindings - registering change notification for multiple keys
- From: dreamcat7 <email@hidden>
- Date: Wed, 2 Jul 2008 11:12:04 +0100
Hi,
Thank you for all of your comments Ken, very helpful.
On 2 Jul 2008, at 09:16, Ken Thomases wrote:
custom class (say, "Preferences") -- again, making the dictionary an
implementation detail -- and making the keys into properties of the
Preferences class. In either case, there should be not key path
which directly accesses the NSMutableDictionary without going
through a custom setter of your own making
Yes that's good advice - it would be better to make a Preferences
object that handled all of the preferences data, and also the saving
and loading operations. Then obj-c properties for the accessor
methods. Thank you for pointing out these missing KVO/KVC
functionalities of NSMutableDictionary. I shall be sure not to use it
except for plist back-end.
Now, how to deal with all those properties? Well, do you really
need for the set of properties to be dynamic, or is it in fact
static and you're just annoyed by the size (and the attendant
repetitive work)?
Yes - its really more about application maintainability than anything
else. I dont want to break the ooc-encapsulation ideaology. And should
not be necessary to re-declare the same variable names in multiple
places. Ultimately, all these items are manipulated by the user
interface through bindings in IB. Otherwise they could be encapsulated
better as feature-oriented objects and would not need to be accessed
from those IB-controls (NSSlider, NSButton, etc) as individually.
If you really need for there to be a dynamic set of properties, you
can accomplish that using valueForUndefinedKey: and
setValue:forUndefinedKey:. Basically, you have "virtual" properties
-- they won't have proper individual accessors, but the above-named
methods will be invoked whenever anything tries to access them via
KVC, and in your implementations you can simulate their existence
(by accessing the NSMutableDictionary which is part of your
implementation, for example)
If i understand correctly what you say its actually possible to
override that function for plist-type object ?
- setValue:(NSObject*) obj ForUndefinedKey: (NSString*) key
{
// If i have an internal representation of NSMutableDictionary for
the .plist file
[self.myMutableDict_PlistStore setValue:obj ForKey: key];
// If property Key is written with a naming convention, e.g. prefixed
with letters 'PTUI'
// We may might assume it is one of our UI Element keys. And we may
include a handler for this weak type.
// We can to call our data store-class methods to implement the
common repetitive functionality**
// e.g. [self savePlistStore]; or we cache the PlistStore and save it
when application becomes idle.**
}
So to confirm it will be triggered whenever any other object tries to
access or save an undeclared property ?
Its also likely that elsewhere in the code accessing these non-
existent properties give rise to some compiler warnings (!! ).
However for a more complex UI and many IB controls / outlets it means
fewer (1) places to repeat that complexity on the model-side.
Certain larger cocoa apps - could benefit by using this approach.
** and in real-world application i no longer need to wire all controls
to target action "uiSateState". Then binding to the undefined key
'PTUI(ElementName)' is neater.
It is weak-typing, but allows dynamic UI elements. And more coherent -
looking code. This [self savePlistStore] is equivalent to synthetic
property anyPreferences ( which Ken describe also for dependantKeys
method below).
A larger and more complex app (e.g. a drawing program with toolbars
and draggable items, etc) - would be an option to consider. Or even to
re-factor an existing app.
Mine isnt particularly big yet but I believe for that others on this
list, they may get into trouble with lots of settings and want to use
something like that.
Whether the set of properties is static or dynamic, it's troublesome
to observe them all. One possible solution for that is to use a
synthetic property which represents the state of the whole set of
properties. This property exists solely so that clients of the
class may watch for changes in this multi-property state. It has no
meaningful value. That is, clients may observe it, but they'll
never really care what its value is, only when it "changes".
Let's suppose you go the route of creating a Preferences class. In
that case, let's call the synthetic property "anyPreferences". It's
read-only, so we just need a simple getter:
-(id)anyPreferences
{
return self;
// The choice to return self is essentially arbitrary
// We could instead return a different NSObject each time: return
[[[NSObject alloc] init] autorelease];
// That would guarantee that any attempt to compare the "old" and
"new" values of this property would indicate a difference,
// but it could cause a spike in memory usage.
}
Now, classes can observe an instance of Preferences for changes in
its "anyPreferences" property. But under what circumstance would
they be notified of a change in that property? Well, the normal
ones. Something should arrange for will/didChangeValueForKey: to be
called on the instance of Preferences with @"anyPreferences" as the
parameter.
In the case of a static set of properties, the best thing to do is
implement +keyPathsForValuesAffectingAnyPreferences in the
Preferences class and return a set of the names of all of those
properties:
+keyPathsForValuesAffectingAnyPreferences
{
return [NSSet setWithObjects:@"debug", /* list the other preference
names here... */, nil];
If i declared my properties in the Preferences class, maybe there's a
way to dump those list in obj-c from the class object and use it here.
If not then it might be a workaround to subclass the NSObject class to
find the list of properties that belong to it.
}
(If you're targeting Tiger, you would instead use
+setKeys:triggerChangeNotificationsForDependentKey: to accomplish
this same thing:
+(void) initialize
{
if (self == [Preferences class])
{
[self setKeys:[NSArray arrayWithObjects:@"debug", /* list the
other preference names here... */, nil]
triggerChangeNotificationsForDependentKey:@"anyPreferences"];
}
}
)
I know this is a lot to take in. I'm verbose like that. Sorry. ;)
I hope it's helpful, though.
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