Monitoring NSMutableArray for insert / delete

The class has a property (and an instance of var) of type NSMutableArray with synthesized accessories (via @property ). If you are observing this array using:

 [myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL]; 

And then insert the object into the array as follows:

 [myObj.theArray addObject:NSString.string]; 

Notification observValueForKeyPath ... not sent. However, the following message sends a proper notification:

 [[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string]; 

This is because mutableArrayValueForKey returns a proxy object that takes care of notifying observers.

But should synthesized accessors not automatically return such a proxy object? How can I get around this - write a custom accessory that just calls [super mutableArrayValueForKey...] ?

+73
cocoa key-value-observing key-value-coding
Nov 19 '08 at 15:54
source share
7 answers

But should synthesized accessors not automatically return such a proxy object?

No.

How can I get around this - write a custom accessory that just calls [super mutableArrayValueForKey...] ?

No. Deploy array accessors . When you name them, KVO will automatically post relevant notifications. So all you have to do is:

 [myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]]; 

and the right thing will happen automatically.

For convenience, you can write the addTheArrayObject: accessory. This accessor will call one of the real array accessories described above:

 - (void) addTheArrayObject:(NSObject *) newObject { [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]]; } 

(You can and should populate the appropriate class for the objects in the array instead of NSObject .)

Then instead of [myObject insertObject:โ€ฆ] you write [myObject addTheArrayObject:newObject] .

Unfortunately, add<Key>Object: and its counterpart remove<Key>Object: are the last ones I checked, only recognized by KVO for set properties (as in NSSet), and not array properties, so you don't get free notifications from them KVO, unless you implement them on top of the accessories that it recognizes. I filed an error about this: x-radar: // problem / 6407437

I have a list of all access selector formats on my blog.

+77
Nov 20 '08 at 2:10
source share

I would not use willChangeValueForKey and didChangeValueForKey in this situation. First, they are intended to indicate that the meaning along the way has changed, and not the meaning change in many ways. Instead, you would like to use willChange:valuesAtIndexes:forKey: if you would do so. However, using manual KVO notifications like this is bad encapsulation. The best way to do this is to define the addSomeObject: method in a class that actually owns the array, which will include KVO notifications manually. Thus, external methods that add objects to the array do not have to worry about how to handle the owner of the KVO array, which will not be very intuitive and may lead to unnecessary code and possibly errors if you start adding objects to the array from several places.

In this example, I would continue to use mutableArrayValueForKey: I'm not sure about mutable arrays, but I believe that after reading the documentation, this method actually replaces the entire array with a new object, so if performance is a concern, you will also want to implement insertObject:in<Key>AtIndex: and removeObjectFrom<Key>AtIndex: to the class to which the array belongs.

+9
Nov 19 '08 at 19:51
source share

when you just want to watch the counter change, you can use the cumulative key path:

 [myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL]; 

but keep in mind that any reordering in the array does not work.

+5
Oct 31 '13 at 14:07
source share

Own answer to your question is almost right. Do not ship theArray externally. Instead, declare another theMutableArray property that does not match the instance variable, and write this accessor:

 - (NSMutableArray*) theMutableArray { return [self mutableArrayValueForKey:@"theArray"]; } 

As a result, other objects can use thisObject.theMutableArray to make changes to the array, and these changes activate KVO.

Other answers indicating that efficiency increases if you also implement insertObject:inTheArrayAtIndex: and removeObjectFromTheArrayAtIndex: are still true. But there is no need for other objects to know about it or call them directly.

+3
Jun 17 '10 at 4:02
source share

If you don't need a setter, you can also use the simpler form below, which has similar performance (the same growth rate in my tests) and less boilerplate.

 // Interface @property (nonatomic, strong, readonly) NSMutableArray *items; // Implementation @synthesize items = _items; - (NSMutableArray *)items { return [self mutableArrayValueForKey:@"items"]; } // Somewhere else [myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items" 

This works because if array accessors are not implemented and there is no key for the key, mutableArrayValueForKey: will look for an instance variable named _<key> or <key> . If it finds one, the proxy will send all messages to this object.

See these Apple docs , โ€œAccess Search Pattern for Ordered Collections,โ€ # 3.

+2
Apr 17 '14 at 1:29
source share

You need to wrap the addObject: call in willChangeValueForKey: and didChangeValueForKey: . As far as I know, for NSMutableArray you cannot find out about any watchers watching their owner.

+1
Nov 19 '08 at 17:49
source share

one solution is to use NSArray and create it from scratch by inserting and deleting, for example

 - (void)addSomeObject:(id)object { self.myArray = [self.myArray arrayByAddingObject:object]; } - (void)removeSomeObject:(id)object { NSMutableArray * ma = [self.myArray mutableCopy]; [ma removeObject:object]; self.myArray = ma; } 

how do you get KVO and can compare old and new arrays

NOTE: self.myArray must not be nil, otherwise arrayByAddingObject: the result is also nil

This may be a solution, depending on the case, and since NSArray only stores pointers, this is not too important if you are not working with large arrays and frequent operations

0
Oct 11 '13 at 8:35
source share



All Articles