• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Observing Collections
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


  • Follow-Ups:
    • Re: Observing Collections
      • From: Hamish Allan <email@hidden>
  • Prev by Date: Re: UNICODE from a WinTel box
  • Next by Date: Re: UNICODE from a WinTel box
  • Previous by thread: Re: Observing Collections
  • Next by thread: Re: Observing Collections
  • Index(es):
    • Date
    • Thread