Re: Observing Collections
Re: Observing Collections
- Subject: Re: Observing Collections
- From: Hamish Allan <email@hidden>
- Date: Wed, 4 Jan 2006 23:14:56 +0000
On Mon, 2 Jan 2006 18:18:56 -0500, Chad Woolf wrote:
I want to observe a key on objects residing in a collection (NSSet
preferably but maybe an NSArray would work too). So, lets say I have
a collection of 10 objects, each having a key "enabled", and I want
to observe the value of the key "enabled" for each object, and also
track new objects coming and going from the collection. (I want to
do this because I want to maintain a separate collection of enabled
objects, and I want to it always be up to date)
In whatever code you are observing objects coming to and going from
the collection, if you're using NSKeyValueObservingOptionOld and
NSKeyValueObservingOptionNew you will have access to arrays
containing any removed or added items, so you can safely add and
remove observers at this point because even if the objects have been
removed from the collection they won't have been released yet because
they will be referenced by these arrays.
This seems fairly fundamental because I've hit this pattern several
times, but I've yet to see an easy way to do it because when you need
to manage object life cycles and add and remove observers fairly
carefully. I've had to create a helper class to implement this
pattern, but I figured people have come across this before and there
is a much more elegant solution.
How would you do it?
Perhaps something like the following?
(Warning: composed in Mail.app, untested, and I've never tried using
"self" as a key path before).
// This subclass of NSMutableSet proxies KVO for its members.
// The method addObserver: forContainedObjectsKeyPath:
// calls addObserver: forKeyPath: for any objects added to the
collection
// and removeObserver: forKeyPath: for any objects removed from it, etc.
@interface NSMutableSetWithObservations
{
NSMutableDictionary *myObservers;
}
- (void)addObserver:(NSObject *)observer
forContainedObjectsKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context;
- (void)removeObserver:(NSObject *)observer
forContainedObjectsKeyPath:(NSString *)keyPath;
@end
@implementation NSMutableSetWithObservations
- (id)init
{
if ((self = [super init]))
{
myObservers = [[NSMutableDictionary alloc] init];
[self addObserver:self forKeyPath:@"self"
options:(NSKeyValueObservingOptionOld |
NSKeyValueObservingOptionNew)
context:NULL];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if (object == self && [keyPath isEqualToString:@"self"])
{
NSSet *observers = [myObservers objectForKey:keyPath];
NSEnumerator *observerEnum = [observers objectEnumerator];
id observer;
while ((observer = [observerEnum nextObject]))
{
NSEnumerator *itemEnum;
id item;
itemEnum = [[change objectForKey:NSKeyValueChangeOldKey]
objectEnumerator];
while ((item = [itemEnum nextObject]))
[item removeObserver:observer forKeyPath:keyPath];
itemEnum = [[change objectForKey:NSKeyValueChangeNewKey]
objectEnumerator];
while ((item = [e nextObject]))
[item addObserver:observer forKeyPath:keyPath
options:options context:context];
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
- (void)addObserver:(NSObject *)observer
forContainedObjectsKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context
{
NSEnumerator *e = [self objectEnumerator];
id item;
while ((item = [e nextObject]))
[item addObserver:observer forKeyPath:keyPath
options:options context:context];
NSMutableSet *keyPathObservers = [myObservers objectForKey:keyPath];
if (keyPathObservers == nil)
{
keyPathObservers = [NSMutableSet set];
[myObservers setObject:keyPathObservers forKey:keyPath];
}
[keyPathObservers addObject:observer];
}
- (void)removeObserver:(NSObject *)observer
forContainedObjectsKeyPath:(NSString *)keyPath
{
NSMutableSet *keyPathObservers = [myObservers objectForKey:keyPath];
if (keyPathObservers)
[keyPathObservers removeObject:observer];
NSEnumerator *e = [self objectEnumerator];
id item;
while ((item = [e nextObject]))
[item removeObserver:observer forKeyPath:keyPath];
}
- (void)dealloc
{
NSEnumerator *keyPathEnum = [myObservers keyEnumerator];
NSString *keyPath;
while ((keyPath = [keyPathEnum nextObject]))
{
NSSet *observers = [myObservers objectForKey:keyPath];
NSEnumerator *observerEnum = [observers objectEnumerator];
id observer;
while ((observer = [observerEnum nextObject]))
{
NSEnumerator *itemEnum = [self objectEnumerator];
id item;
while ((item = [itemEnum nextObject]))
[item removeObserver:observer forKeyPath:keyPath];
}
}
[myObservers release];
[super dealloc];
}
@end
Best wishes,
Hamish
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden