I'm confused by this: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/AccessorConventions.html#//apple_ref/doc/uid/20002174-178830-BAJEDEFB
Supposing
#interface Office : NSObject {
NSMutableArray *employees;
}
What is the benefit of implementing the collection accessors?
How is [anOffice countOfEmployees] better than [[anOffice employees] count]?
Do bindings depend on the collection accessors or can I forego them completely?
They seem redundant to me since I'm using a true array object. I can understand how they would be needed if employees wasn't an NSMutableArray and didn't implement something like a count method itself.
I'm also absolutely stumped by why would would use mutableArrayValueForKey:#"employees" for fetching the employees property instead for simply valueForKey:#"employees".
Thanks!
You can forego the collection accessors; they aren't required. But they make things a lot easier.
One reason to have them, including countOfEmployees, is efficiency: The employees method may return a copy of the array object (particularly since the Office's copy is mutable, so the Office would not want other objects mutating the array out from under it), but if you only need to know the count or to access one object at a specific index, you don't need a copy.
The other reason is when the sender wants to mutate the property.
valueForKey: will call employees, which will ordinarily return an immutable copy.
Returning a mutable copy would not help, since mutating that array would be mutating the copy, not the original through the property.
Returning the original array will not enable the sender to cause KVO notifications for its changes, so nothing observing the property will know about those changes. This means the values shown in your UI will go stale (not be updated).
mutableArrayValueForKey: returns a fake array that sends mutation messages (or, if nothing else, employees and setEmployees: messages) back to the original object. Accessor messages do cause KVO notifications, so anything observing the property will follow along with these changes, so your UI keeps up to date.
Of course, you could just send the accessor messages yourself. mutableArrayValueForKey: is mainly for if you want to make changes to a property that isn't known at compile time; NSArrayController is, presumably, one user of this method. You aren't likely to need to use mutableArrayValueForKey: in a regular application, and sending accessor messages yourself is, in my opinion, easier to read.
All of this goes for the Office as well, when it mutates its own array. It could just talk to its array object directly, but that wouldn't cause KVO notifications, so nothing else would know the value of the property had changed. You could post the KVO notifications yourself around each change, but that's a hassle and easy to forget. Collection accessors and mutableArrayValueForKey: are two solutions to these problems: Each access is a single line of code that will cause KVO notifications.
Related
I have subclassed NSMutableArray to allow for a datasource. This is called BaseObjectArray. The array actually only holds a list of rowids (as uint64_t), and when asking for objectAtIndex it asks the datasource delegate for the object with that rowid (to allow for lazy DB queries).
The internal list of rowids is a class in it's own right (a RowIDSet, or the OrderedRowIDSet subclass, which is just a subclass of NSObject), that maintains just the list of unique rowids.
What I need is to somehow listen for changes to the BaseObjectArray (which is actually listening to changes on it's RowIDSet object, perhaps through a similar method).
As objects may be added/removed from the BaseObjectArray not using the standard addObject:, but instead with addRowID:, the object that owns the BaseObjectArray will probably not get standard KVO notifications.
Possible solutions I have considered:
The BaseObjectArray has owner and ownerKey properties, and the BaseObjectArray triggers [owner willChangeForKey:ownerKey]; whenever anything changes.
Use will/didChangeNotificationBlocks - listeners can simply add a block to the BaseObjectArray (retaining these blocks in an NSMutableArray), and all the blocks in this array are triggered when something in the BaseObjectArray changes. I am uncertain about the possible retain-cycle nightmare that may ensue.
KVO on a 'contents' property of the BaseObjectArray. Anyone wanting to observe the BaseObjectArray actually observes the keyPath 'contents', and inside the BOArray it calls [self willChangeForKeyPath:#"contents"]. The contents property just returns self.
... something obvious that i have missed ...
Please let me know if any of these make the most (or any) sense, or if there is a better solution out there.
Thanks :)
Unless you know what you are doing, you should not subclass NSMutableArray. NSMutableArray is a class cluster and requires special treatment.
Why not just create a custom object that uses a plain NSMutableArray as its storage class? There seems to be no good reason to subclass NSMutableArray in your case, but maybe I'm misunderstanding your question.
I don't know if this will work, but if it does, it's probably the best way.
Make sure your NSMutableArray subclass is KVC compliant for the key self (if this doesn't work for self add a new property e.g rows which returns self or a copy of self). To make self (or whatever new property you use) KVC compliant you need to follow the Indexed To-Many Relationship Compliance rules for mutable ordered collections:
Implement a method named - that returns an array.
Or have an array instance variable named or _.
Or implement the method -countOf and one or both of -objectInAtIndex: or -AtIndexes:.
Optionally, you can also implement -get:range: to improve performance.
self ticks the box on the first of these. Also:
Implement one or both of the methods -insertObject:inAtIndex: or -insert:atIndexes:.
Implement one or both of the methods -removeObjectFromAtIndex: or -removeAtIndexes:.
ptionally, you can also implement -replaceObjectInAtIndex:withObject: or -replaceAtIndexes:with: to improve performance
So you'll need e.g. -insertObject:inSelfAtIndex: and -removeObjectFrom<Key>AtIndex:
Then you can use manual KVO notifications wherever you want to notify obeservers of the self property on that object. So you might use
NSIndexSet* indexes = // index set containing the index or indexes of objects to remove
[self willChange: NSKeyValueChangeRemoval valuesAtIndexes: indexes forKey:#"self"];
when removing objects.
I have some values that are computed over collections, and may or may not be displayed (and thus may or may not have an observer) at any given time. I would rather not have to track all the members of the collection if nobody is observing my computed values.
Can I tell if anyone is currently observing a value, and can I tell when they start observing?
I know for a given object foo I can use [foo observationInfo] to get a list of observers with key paths registered with a root at foo, but that doesn't automatically get all paths TO foo (in fact it only gets ones registered to observe foo's self key).
That’s not a good idea from the design point of view. If you really insist on not updating the contents when nobody needs them (which could be a legitimate case, for example if the updates are expensive), you can introduce methods to start/stop the updates:
- (void) beginUpdatingContents;
- (void) endUpdatingContents;
These should be tied to a counter inside the class and if the counter is > 0, you know somebody wants to keep the contents updated. This solution is explicit and therefore better than silent magic with KVO.
If you want more magic, how about overriding addObserver:forKeyPath:options:context: and removeObserver:forKeyPath: and tracking what is still observing you?
The way I've actually done this in the past is by making wrapper objects (I called them bindings) which set up KVO and also register themselves with the target. So, a user would call MyBinding *binding = [targetObject bindKeyPath:#"foo" ...] and then later [binding detach]. You then have the binding use KVO under the hood and keep a list of themselves so you know when it's empty.
Let's suppose I create a few objects and I add them to an array.
House *myCrib = [House house];
House *johnHome = [House house];
House *lisaHome = [House house];
House *whiteHouse = [House house];
NSArray *houses = [NSArray arrayWithObjects: myCrib, johnHome, lisaHome, whiteHouse, nil];
Normally, all House objects have a retain count of two, but they're being autoreleased once. After a while, I decide to release myCrib, even if I'm not the owner — I never retained or initialized.
[myCrib release];
The retain count should drop to zero and my object should be deallocated. My question now is: will this illegal action cause my app to work erroneously or even crash, or will NSArray simply delete my object from its list with bad consequences.
I'm looking for a way to maintain a list of objects, but I want the list to maintain itself. When some object disappears, I want the reference to it to disappear from my array gracefully and automatically. I'm thinking of subclassing or wrapping NSArray.
Thank you.
My question now is: will this illegal
action cause my app to work
erroneously or even crash, or will
NSArray simply delete my object from
its list with bad consequences.
Your array now has an invalid object pointer. There's no way to tell that the pointer is invalid just by looking at it, and the array isn't notified that the object has been deallocated. The problem isn't with the array, after all, the problem is with the code that improperly releases the object. So yes, the application will likely crash or otherwise behave incorrectly due to that bad pointer, and no, NSArray won't detect and deal with the problem for you.
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically.
If the objects in the list are all instances of a common class, you could define your own memory management methods that both retain/release the object and add/remove it from the list, or broadcast appropriate notifications in case there can be multiple lists. I suppose you could even override -retain and -release for this purpose, but I'd think long and hard about that before doing it, and document it well if you do; it's not the sort of thing that other developers would expect.
Another option might be Core Data. If you delete a managed object from the object graph, it'll disappear from any relationships. Strictly speaking, a to-many relationship is a set, not a list, but the difference may not be a concern for your purposes.
Update: I just noticed that you didn't tag your question ios. If you're working under MacOS X, you should definitely take a look at NSPointerArray. If you use garbage collection, NSPointerArray can be configured to use weak references and to replace references to collected objects with null references. This is exactly what you seem to be looking for.
You should not release myCrib if you are not the owner. To do so is a violation of the memory management guidelines and will make your code extremely difficult to maintain. I cannot stress enough that you absolutely should never do this under any sort of circumstance. You're asking for crashes; the array has declared ownership of the object, and you must not subvert that ownership in any way.
So the answer here is: your code is absolutely wrong and you should fix it. If you can't fix it, you should trash it and start over and keep rewriting it until you've come up with another way to achieve the same effect without subverting object ownership. I guarantee that it's possible.
If what you want is a weak-referencing array, then there are a couple ways you can do this (this was just asked a couple of days ago):
NSPointerArray - weakly references its pointers. When you use garbage collection, they're autozeroing (ie, the pointers get removed when the object is deallocated). Unfortunately, this is not available on iOS.
CFMutableArrayRef - you can specify a custom retain and release callback, or just not specify one at all. If you leave them out, the array will simply not retain the objects it contains. However, this does not automatically remove the pointer when the object is deallocated.
DDAutozeroingArray - an NSMutableArray subclass I wrote the other day to provide a weakly-referencing and auto-zeroing array that works on both Mac OS and iOS. However, I strongly encourage you to use this only as a last resort; There are probably much better ways of doing what you're looking for. https://github.com/davedelong/Demos
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically. I'm thinking of
subclassing or wrapping NSArray.
If I have understood right, what you want is an array of weak references. Then, you might be interested in reading this post.
You're asking for a crash here. Your NSArray will still have a reference to the object that now no longer exists -- and who knows what it will be pointing to after a while?
Subclassing NSArray might not be the answer either. It's a class cluster which, in short, means that it's harder to subclass than you might hope.
Not entirely sure how you'd implement this. Something like the element sending a notification when they're about to be deallocated which the array would then pick up. You'd need to be careful that you didn't leak or over-release your objects.
I created a wrapper class — in my code it's called a controller — which maintains the (mutable) array for me. I initialize the controller class in my view controllers — the place where I need them — instead of using an array directly.
No invalid code for me. :-p
My question, in brief: Is there any way to make mutable collection KVO accessors thread-safe with the same lock that the #synthesized methods are locked?
Explanation: I have a controller class which contains a collection (NSMutableArray) of Post objects. These objects are downloaded from a website, and thus the collection changes from time to time. I would like to be able to use key-value observing to observe the array, so that I can keep my interface updated.
My controller has a posts property, declared as follows:
#property (retain) NSMutableArray *posts;
If I call #synthesize in my .m file, it will create the -(NSMutableArray *)posts and -(void)setPosts:(NSMutableArray *)obj methods for me. Further, they will be protected by a lock such that two threads cannot stomp on each other while setting (or getting) the value.
However, in order to be key-value coding compliant for a mutable ordered collection, there are a few other methods I need to implement. Specifically, I need to implement at least the following:
-insertObject:inPostsAtIndex:
-removeObjectFromPostsAtIndex:
However, since the posts are downloaded asynchronously, I would like to be able to insert new posts into the array on a background thread as well. This means that access needs to be thread-safe.
So, my question. Is there any way to make those accessors thread-safe with the same lock that the #synthesized methods are locked? Or do I have to resort to specifying the setPosts: and posts methods myself in order to guarantee full atomicity across all accessors?
The Objective-C docs at developer.apple.com[1] don't state that there's a way to use the same lock for your explicitly defined functions as gets used for your #synthesized functions. In that case I'd say that to be completely safe it would be better to fully define your own functions to be sure they all use the same lock.
You may be able to use the debugger to determine the name of the lock that gets used for your #synthesized functions, but that's not something I'd rely on.
You probably don't really want to do this. If you do succeed, KVO-notifications will be received on the same thread that makes the change, and if it's a background thread, will be unsuitable for updating the UI.
Instead, why not have your background thread update the property using the main thread? Then you don't even need the property to be atomic.
I am smitten by KVC/KVO. Super powerful. There is one problem though. I'm trying to be true the the MVC etho but I see no way to use an observation pattern to monitor the allocation or deallocation of an Objective-C class instance.
This is actually important as I have a model with fine-grained internal messaging that I want to observe from a controller (or delegate). The stumbling block for me is I don't see how, external to the model, I can remove an observer for a sub-component that is about to be deallocated without the controller knowing about the internal logic of the model which would compromise encapsulation.
Can someone suggest an approach for this scenario.
Thanks,
Doug
Doug - there really isn't enough information in your description to know what it is you are doing and how to best (or if it is appropriate at all) apply KVO to the problem.
KVO is all about observing properties on objects. You typically shouldn't care when they are created or destroyed except insofar as you must stop observing them before they are destroyed.
You should instead start and stop observing objects when those objects become interesting to you. Consider a graphics drawing package where a document has an ordered array of shapes, and you are interested in observing the backgroundColor property of each shape.
We wouldn't try to observe the instantiation and deallocation of the Shape instance, but instead we observe the "shapes" property on the document. Through that observer, we can determine when a shape is added to, or removed from, the document. When a shape is added to the document, we start observing it. When it is removed from the document, we stop observing it. (Note that it may be removed from the document but not deallocated, if it is on the undo stack, etc.)
In the object graph for your model, to use KVO you'll want to add and remove the objects from your object graph in a KVO compliant way so you can observe the relationship mutations, and in that observer, start and stop property observers on the related objects.
I think you'll have to post the notifications yourself, unless you use something like CoreData. If you're using CoreData, NSManagedObject (the root class of all stored CoreData objects) has an -awakeFromInsert method that gets called after the object has been created and inserted into the ManagedObjectContext.
As for destruction, you could probably just post a notification right as you enter the -dealloc method.
I'm not sure exactly what you're trying to achieve, so a little more explanation would be good.
If you just want to remove an observer before the observed object gets deallocated, then don't worry, because KVO will handle it. Even if you're using notifications it won't cause a problem, you just won't receive any notifications from the object.
If you're trying to observe multiple objects (e.g. an array of Widgets), and would like to know when an object is added or deleted, KVO can handle that too. You just have to make the array a key on your model object, and observe it with KVO. You also have to modify the array in a KVO compliant way (e.g. mutableArrayForKey:, or use your own willChangeValueForKey and didChangeValueForKey).