KVO and NSMutableArray - objective-c

How can I set KVO (key-value-observing) with an NSMutableArray?
I want to be notified when a change appears in the array. I never used KVO before with a collection like an array.

I think you'll be interested in the answers to this question.
The key is that you can't observe any properties on the array directly—the array is just storage—but you can observe the to-many relationship that's backed by that array (here I'm assuming your array is a property on an object somewhere).
If you don't want to use those special accessors all over the place, your code that owns the array can call
-willChange:valuesAtIndexes:forKey: and
-didChange:valuesAtIndexes:forKey:
as described in this answer.
And if you're on a Mac and not iOS, you should consider NSArrayController.

You can use KVOMutableArray.
It is a subclass of NSMutableArray, and it supports KVO.
disclaimer: I am the author :)

Related

Change notification for self in NSMutableDictionary subclass

I've subclassed NSMutableDictioary following mainly this great blog post.
Now in that subclass the dictionary itself subscribes to all its values in order to detect any changes. This bit works just fine and value changes are observed internally in that subclass.
However value changes should then be propagated to observers of the dictionary, i.e. observers of the dictionary (subclass) are supposed to get notified that the dictionary has changed.
Except I cannot figure out how to trigger that KVO notification - using
[self didChangeValueForKey:#"self"];
doesn't seem to have any effect so I'm kinda stuck.
How can an instance of the NSMutableDictionary subclass initiate a change notification for itself?
As already hinted by Ken KVO allows you to observe properties - not an object per se!
The Key-Value Observing Programming Guide states, that
Key-value observing provides a mechanism that allows objects to be notified of changes to specific properties of other objects.
In other words: self can never be observed.

Put NSRects into an array, as objects?

I want to make a NSMutableArray and fill it up with rectangles created with, say, NSRectMake. But the array only allows objects. What should I do to save the NSRects in an array?
Use NSValue that's especially designed for that stuff. In your case, use valueWithRect:
Everything is explained here in Apple's corresponding Programming Guide.

Share NSArray instance to KVO objects

I have a controller class that store a list of contact and I need to share this list to objects that will implement KVO on such array.
The most obvious solution is to use a NSArray instead of NSMutableArray, in order that everyone can use the same instance variable of the controller and it's not possible modify the content of the array.
Now, let's image that this list can change every 3 minutes and only the controller can update the content of the array.
If I dealloc and re-init the contact list array, all my KVO objects will lost the reference to my array.
Finally:
NSArray PRO: No one can alter the content of the array
NSArray CON: The controller can not modify the array without a dealloc, so other objects lost the reference.
NSMutableArray PRO: Controller can modify array content, all the object can easily share the same reference of the array
NSMutableArray CON: Everyone can modify the array.
Any suggestion?
NSArray in my opinion, given that it isn't being updated very often.
The object that owns and modifies the array exposes it through a property of type NSArray*. The observers observe this property. Whenever the object that owns the array recreates it, it sets the property to the new NSArray which then triggers KVO on all of the observers.
Alternatively, you can implement the indexed accessor patterns for the property.

How to add alive object to NSMutableArray and remove them when they're released?

I have class Item and class List (which has an NSMutableArray).
Every time class Item is instantiated (and destroyed) it posts a notification, which is listened-to by class List. When class List receives the notification is adds the instance of class Item to its list.
I'm trying to have class Item also post a notification that its about to be dealloc'd. The problem is that class List's NSMutableArray retains the instance of class Item.
What's the most appropriate means of handling this situation? If I decrement the count when adding it to List's array, then an exception will be thrown when class List attempts to call removeObject (since it'll try to dealloc the object.)
Basically, I want a "monitor" class List that contains a list of all "live" instances of Item. But, I also need the ability to release/dealloc the instances and have them report they're being dealloc'd so List can remove them from its NSMutableArray.
Thanks for your help.
If I understand correctly, you want an array that maintains weak references to its items, as opposed to strong references?
I don't know of a way to do this with anything "built-in" in Cocoa. The only way I'd know of to do this is to make the array yourself, and have the storage be __weak id[]. That would automatically zero-out the place in the array when the object deallocates. If you're under the retain-release model, you could use something like MAZeroingWeakRef to get the same behavior.
This is definitely an interesting question, and I don't know of an easier answer. I'd love to be proven wrong!
Ha, I love being wrong!
There's a class called NSPointerArray that looks like it can do what you're looking for. However, it's only available on the Mac, and it only auto-zeros when you're using garbage collection.
I'll keep thinking about this. This is an interesting problem! :)
So I kept thinking about this, and came up with a solution. It uses two unconventional things:
A subclass of NSMutableArray (egads!)
Using an associated object to determine object deallocation
For the first bit, I had to to subclass NSMutableArray so that I could inject some custom logic into addObject: (and related methods). I didn't want to do this via swizzling, since NSArray and friends are a class cluster, and swizzling into/out of clusters is fraught with peril. So, a subclass. This is fine, but we're going to lose some of the awesome features we get from "pure" NSArray instances, like how they do weird things when they get big. Oh well, such is life.
As for the second bit, I needed a way for any arbitrary object to notify that it is about to or just finished deallocating. I thought of dynamically subclassing the object's class, injecting my own dealloc/finalize method, calling super, and then smashing the isa of the object, but that just seemed a little too crazy.
So, I decided to take advantage of a fun little thing called associated objects. These are to ivars what categories are to classes: they allow you to dynamically add and remove pseudo-instance variables at runtime. They also have the awesome side effect of getting automatically cleaned up with the object deallocates. So what I did is just created a little throw away object that posts a notification when it is deallocated, and then attached it to the regular object. That way when the regular object is deallocated, the throw away object will be as well, resulting in a notification being posted, which I then listen for in the NSMutableArray subclass. The notification contains a (stale) pointer to the object that is in the process of getting destroyed, but since I only care about the pointer and not the object, that's OK.
The upshot of all of this is that you can do:
DDAutozeroingArray *array = [DDAutozeroingArray array];
NSObject *o = [[NSObject alloc] init];
[array addObject:o];
NSLog(#"%ld", [array count]); //logs "1"
[o release];
NSLog(#"%ld", [array count]); //logs "0"
The source is on github, and it should (theoretically) work just as well on iOS as Mac OS X (regardless of GC mode): https://github.com/davedelong/Demos
Cheers!
... and I just thought of a way to do this without a custom subclass, but I'm tired and will post the updated answer tomorrow.
the next morning...
I've just updated the project on Github with an NSMutableArray category that allows you to create a true NSMutableArray that auto-zeroes its objects as they're deallocated. The trick was to create a CFMutableArrayRef with a custom retain callback that sets up the proper observation, and then just cast that CFMutableArrayRef to an NSMutableArray and use that (ah, the magic of Toll-Free Bridging).
This means you can now do:
NSMutableArray *array = [NSMutableArray autozeroingArray];
I added a typedef to define these as NSAutozeroingMutableArray, just to make it explicitly clear that while this is an NSMutableArray, it doesn't retain its objects like a normal NSMutableArray. However, since it's just a typedef and not a subclass, you can use them interchangeably.
I haven’t tested this, so comments are welcome.
You could use an NSPointerArray for the list (in a retain property):
self.array = [NSPointerArray pointerArrayWithWeakObjects];
When an Item object is created, it would post a notification that’s listened by your List class. Upon receiving the notification, List adds the object to the pointer array:
[array addPointer:pointerToTheObject];
In this setting, the pointer array doesn’t keep a strong reference to its elements — in particular, it doesn’t retain them. This applies to both garbage-collected and non-garbage-collected builds.
In a garbage-collected build, if an element is garbage collected then the garbage collector automatically assigns NULL to the position in the array where the object was stored.
In a non-garbage-collected build, you’ll need to manually remove the element or assign NULL to the position in the array where it was stored. You can do this by overriding -[Item dealloc] and posting a notification that the object is being deallocated. Your List class, upon receiving the notification, would act upon it.
Note that, since objects are not owned by the pointer array, you must keep a strong reference to (or retain) them if you want to keep them alive.

Cocoa Touch Question. Using KVO in a touch sequence context

I would like to use KVO in the following context:
1) In touchesBegan:withEvent: I alloc/init an instance of an object that I then observe via KVO
My intent is to observe varous behaviors of the object throughout its life time.
2) In touchesEnded:withEvent: I assign this instance to an NSMutableArray and release the instance reference since NSMutableArray now retains it. I also must remove the oberver of the instance via removeObserver:forKeyPath:
This is problematic because I now have lost all observation unless I add the observe back again to the array element which smells bad.
Is there a way to have the observer remain attached to the object regardless of who owns it?
Thanks,
Doug
In Objective-C, you don't "own" an object, you merely have a claim on it. You don't need to release the instance just because the NSMutableArray retains it -- you can both have a claim on it. When you've finished with the object, remove yourself as an observer and release the object. When you've finished with the NSMutableArray, release that. This way, everything takes care of itself.