How to receive notifications from an NSMutableArray subclass - objective-c

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.

Related

Is there a way to tell when KVO starts/ends for a particular value?

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.

How to perform an operation before any #synthesize'd accessor?

My model objects are lazy-loaded from an SQLite database. Don't ask why, but the code uses QuickLite to populate the objects, which means that some housekeeping has to be performed before an accessor is used the first time.
I thought, naively, that valueForKey: and setValue:forKey: would be called by the #synthesize'd accessors, so that I could simply overload those 2 methods to fill the object from the db, if necessary. Unfortunately, that doesn't work: the #synthesize'd accessors clearly don't use KVC to get/set their represented value.
My question is therefore: Is there a way to call some code before any #property is accessed, without writing all getters/setters myself?
If your model objects were a subclass of NSManagedObject then your accessors would be using KVC (you declare the properties, then use '#dynamic' rather than '#synthesize' in the .m file to indicate that the accessors will be taken care of by other code).
Basically it sounds like you're re-implementing the faulting behaviour in Core Data.
Based on your comment, the only way I can think of doing this would be to have a sort of proxy object which contains your actual object. So, your proxy object would have a single visible property, which is your actual object, and in the accessor for that, you would then check to see if you'd gone to the database for this particular object, if not, do your housekeeping.
So, your calls would be
NSString *someProperty = proxyObject.realObject.someProperty;
Within proxyObject, the accessor for realObject:
if (beenToTheDatabase)
return realObject;
else
{
// Do your business
beenToTheDatabase = YES;
return realObject;
}
Whether this is more or less effort than manually writing your accessors or migrating to core data, I don't know.

Key-value coding for mutable collections & atomic accessors

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.

KVC array: getters vs indexed accessors?

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.

Using non-copyable object as key for NSMutableDictionary?

I tried to figure out this code referencing: Cocoa: Dictionary with enum keys?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:#encode(id*)];
}
And,
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
But it feels something not good. Any recommendations?
You're absolutely right it's not good.
For one, you're encoding the wrong type (it should be #encode(id), not #encode(id*)), but in most cases this shouldn't cause a big problem.
The bigger problem is that this completely ignores memory management. The object won't be retained or copied. If some other code releases it, it could just disappear, and then your dictionary key will be a boxed pointer to garbage or even a completely different object. This is basically the world's most advanced dangling pointer.
You have two good options:
You could either add NSCopying to the class or create a copyable subclass.
This option will only work for objects that can meaningfully be copied. This is most classes, but not necessarily all (e.g. it might be bad to have multiple objects representing the same input stream)
Implementing copying can be a pain even for classes where it makes sense — not difficult, per se, but kind of annoying
You could instead create the dictionary with the CFDictionary API. Since Core Foundation types don't have a generic copy function, CFDictionary just retains its keys by default (though you can customize its behavior however you like). But CFDictionary is also toll-free bridged with NSDictionary, which means that you can just cast a CFDictionaryRef to an NSDictionary* (or NSMutableDictionary*) and then treat it like any other NSDictionary.
This means that the object you're using as a key must not change (at least not in a way that affects its hash value) while it's in the dictionary — ensuring this doesn't happen is why NSDictionary normally wants to copy its keys
For the later reference.
Now I know that there are some more options.
Override methods in NSCopying protocol, and return the self instead of copying itself. (you should retain it if you are not using ARC) Also you ensure the object to always return same value for -hash method.
Make a copyable simple container class holds strong reference to the original key object. The container is copyable but, it just passes original key when it being copied. Override equality/hash methods also to match semantics. Even just an instance of NSArray contains only the key object works well.
Method #1 looks pretty safe but actually I'm not sure that's safe. Because I don't know internal behavior of NSDictionary. So I usually use #2 way which is completely safe in Cocoa convention.
Update
Now we Have NSHashTable and NSMapTable also in iOS since version 6.0.
I'm not 100% sure about the correctness of this solution, but I'm posting it just in case.
If you do not want to use a CFDictionary, maybe you could use this simple category:
#implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
[self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}
- (id)objectForNonCopyableKey:(id)aKey {
return [self objectForKey:[NSValue valueWithPointer:aKey]];
}
- (void)removeObjectForNonCopyableKey:(id)aKey {
[self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
#end
This is a generalization of a similar method I saw online (can't find the original source) for using an NSMutableDictionary that can store objects with UITouch keys.
The same restriction as in Chuck's answer applies: the object you're using as a key must not change in a way that affects its hash value and must not be freed while it's in the dictionary .
Also make sure you don't mix -(void)setObject:(id)anObject forNonCopyableKey:(id)aKey and - (id)objectForKey:(id)aKey methods, as it won't work (the latter will return nil).
This seems to work fine, but there might be some unwanted side effects that I am not thinking of. If anybody finds out that this solution has any additional problems or caveats, please comment.