Key-Value Observing an NSMutableSet - objective-c

In a plain class I have a NSMutableSet property. Whenever objects are added to or removed from the set, I want to perform some custom code. I know I could write a few addObjectToSet:-like methods to the class, but I was wondering if there's a more elegant solution with direct KVO on the set.
As it turns out, NSSet will raise an exception when you try to add an observer to it. Not surprisingly, for there's probably no named keyPath to observe. The documentation is pretty clear about the exception, but I don't understand the suggested workaround:
Instead of observing a set, observe the unordered to-many relationship for which the set is the collection of related objects.
Could someone reiterate what this means? And what a workaround would then look like?

That's a pretty dense way of saying "don't add an observer to the set itself, add an observer to the class that contains the set":
[myObjWithASetAsIvar addObserver:self
forKeyPath:#"nameOfIvarHoldingTheSet"
options:NSKeyValueObservingOptionNew
context:nil];
The one tricky bit is that you need to wrap all your accesses to the set in order for the proper notifications to be sent. In the class containing the set:
[self willChangeValueForKey:#"nameOfIvarHoldingTheSet"];
// Do something with the set
[self didChangeValueForKey:#"nameOfIvarHoldingTheSet"];
There are also two notification methods specifically for sets: willChangeValueForKey:withSetMutation:usingObjects: and didChangeValueForKey:withSetMutation:usingObjects:; you may find that they work better for you than the generic "value change" methods.
All that said, I believe that the solution you mentioned in your first paragraph, and outlined by Peter Hosey in the question Girish linked to, is probably the best way to go.

From Apple's documentation on NSSet (and, by inference and implication, NSMutableSet):
NSSet objects are not observable, so this method raises an exception
when invoked on an NSSet object. Instead of observing a set, observe
the unordered to-many relationship for which the set is the collection
of related objects.
I'm glad you seem to have found an answer, but the fact that your initial approach was flawed (in that you assumed you could use addObserver on an NSSet), this needed to be mentioned, regardless.

It's actually pretty clear when you relate it to an example:
If you have a class Bank that has a NSSet of Accounts, don't add the observer on Bank.accounts, add the observer on Account.bank

Related

View Key value observations

How do I know what items my object is key-value observing?
The only way I've been able to find out if I'm already observing is to try to remove the observation. If an exception is thrown, then I wasn't observing.
for (AVPlayerItem *item in itemsToRemove) {
#try{
[item removeObserver:self forKeyPath:#"status" context:(__bridge void *)(foo)];
}#catch(id anException){
//wasn't observing
}
}
EDIT: I'm considering using my own dictionary to track observation but that seems redundant since a KVO Dictionary does exist somewhere. Unfortunately there is no API access.
It seems there is no other option than catching the exception, even NSHipster recommends to do so. However, at least in my case, it was hardly ever needed to do the check. After all, you are the one who controls the observers.
You can use a wrapper (like FBKVOController) which adds more sanity to the raw KVO (and makes observing a lot easier, allowing to use blocks). Among other features, it doesn't crash when trying to remove a nonexistent observer:
#discussion If not observing object key path, or unobserving nil, this method results in no operation.
*/
- (void)unobserve:(id)object keyPath:(NSString *)keyPath;
There is no way to know that until you add some boolean flag to your controller and use it to mark and check if your registered for KVO. Normally you should balance out registration and unregistration from KVO observation.
Using exception under ARC is bad and may lead to memory leaks until you use -fobjc-arc-exceptions.
Long story short: Exceptions are expensive, that's why ARC does not properly handle them until you explicitly ask. There is an explanation to that: https://stackoverflow.com/a/4649234/351305

When to use NSNotificationCenter

I want to have multiple observers on multiple events of a single object (1-to-N relationship).
A mechanism to achieve this task is provided by the NSNotificationCenter. The mechanism looks pretty overkill when used for my problem.
How I would do it manually without the use of NSNotificationCenter:
- (void)addDelegate:(id<DelegateProtocol>)delegate;
- (void)removeDelegate:(id<DelegateProtocol>)delegate;
to add and remove observers from my object.
- (void)someEventFired:(NSObject<NSCopying> *)eventData
{
for (id delegate in delegates) {
NSObject *data = [eventData copy];
[delegate someEventFired:data];
}
}
This mechanism is straight-forward and simple to implement without the objects having to share additional strings.
Is there an official pattern for 1-to-N delegates (like C# events) in an iOS framework besides the NSNotificationCenter?
When should the NSNotificationCenter be used and when not?
When should an implementation like the one I am suggesting here be used and when not?
By convention, delegates should probably only be used for 1:1 relationships. If you really need 1:N relationships for this type of functionality, you have two options:
As you mentioned, NSNotificationCenter.
Key-Value Observing (also known as KVO).
KVO is appropriate if you only care about when a particular property of an object changes. Otherwise, you should really just consider using NSNotificationCenter. You can even be notified only when a specific object posts that notification by passing that object into the addObserver:selector:name:object: method.
Apple uses NSNotification in similar scenarios (like the notifications defined for UITextField, including UITextFieldTextDidBeginEditingNotification, UITextFieldTextDidChangeNotification, and UITextFieldTextDidEndEditingNotification).
using notifications is broadcasting: 1 sender just sends an information and who ever tuned in, receives it. Petty much like a radio station, there is no channel back (lets for the moment forget about telephones)
delegation is something different. Th object, that asks a deleagte to do something, usually needs a result of that request, there fore delegation is a 1-to-1 communication, that is always initiated by the object, not the delegate (while the object can have methods that can be called to inform the object to initiate the communication, ie [tableView reloadData]).
So if the sender needs to get data back, it is delegation. If the sender doesn't care about anything after broadcasting, go with notifications.
If you run into the situation, that you need delegation, but several objects should implement the protocol. you should have 1 delegate, that hold references to the other objects and calls the methods on the senders behalf — or you could go with blocks.
NSNotificationCenter is not overkill for what you are suggesting, it is exactly the right solution. It prevents the observed object having to know or care about its observers, making your code more loosely coupled and cleaner.
Sharing strings for notification names is trivial and they can be defined in either a shared constants file or in the header of the observed object, if your observers need to import this header to do their jobs.
Your proposed solution is neither simpler than using NSNotificationCenter nor is it thread safe.
To make your solution thread safe, you would need to provide a mechanism to prevent the delegates array from changing while the event dispatch for loop is running.
Your solution also requires that you maintain the delegates array in your class. With the NotificationCenter you can simply use the default center and you don't need to implement the add/remove methods in your class. Instead, instances can register themselves to receive notifications as they see best fit (selector/block, queue, source). Your source class doesn't have to worry about those details. It only needs to register itself as a source of notifications of a specified type. Using blocks to handle notifications is really convenient.
An alternative to the notification center is to use Key-Value-Observing if that meets the needs of your use case.
Ultimately, the mechanism you decide to use depends on how best it applies to your specific use case.
A 1-to-N delegate relationship doesn't make sense. Have a look at
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
for example. What if this object really had n delegates? How should it decide which of the n views it gets back from all its delegates should be used? Delegates are exactly this 1-to-1 principle.
The NSNotificationCenter is the right approach. Simply use
addObserver:selector:name:object:
respectively
postNotification:
This is definitely not too much code. And it's very easy for you as the center handles all calls.
You don't want to use NSNotificationCenter for anything other than system-wide events (e.g. the appearance of the keyboard or some similar event). The reason is that it is completely not type-safe, can make everything dependent on everything and that you get no compile time checks or usage search results anymore.
KVO in my opinion should not be used to observe changes outside of the object you're listening to since it has similar down sides (no compile time checks, crashes if you don't remove listeners properly or register them twice).
The addDelegate/removeDelegate pattern that you pose is completely the right path in my opinion since that has the advantage of maintaining type-safety and compiler checks and makes dependencies explicit. The only problem is that Apple doesn't supply an out-of-the-box solution for this pattern, since you need a collection type that weakly retains its elements to avoid retain cycles.
However, see code from my BMCommons framework which solves this problem neatly using BMNullableArray and macros. See the BMCore.h header for a definition of those macros:
BM_LISTENER_METHOD_DECLARATION(protocol)
BM_LISTENER_METHOD_IMPLEMENTATION(protocol)
The implementation ensures that the same listener will never be added twice and also that listeners are weakly retained, not causing any crash even if they forget to deregister themselves upon deallocation (although I prefer to catch this condition with an assert since it is a programming mistake).
I say NSNotificationCenter should ALWAYS be used, over the delegate model, except in situations where you query a delegate on information (e.g. -webView:shouldLoadRequest:). It is more stable, easier to implement, and results in cleaner code then trying to use a delegate. The other alternative is blocks, which can be good, but they can be a pain when it comes to memory-managment.
In the end, it's up to you, but I think that NSNotificationCenter is the best way to go in almost any situation, if only for the multiple observer functionality.

How to receive notifications from an NSMutableArray subclass

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.

KVO: observing global NSNumber

I have static global NSNumber value and i need to observe it. If it was member of some object, i would have no problems whatsoever. But what do i do with global scope? I guess i could use
[globalVar addObserver:self forKeyPath:**integerValue** options:... ]
but that seems ugly because i might as well use "intValue" KeyPath and i need to observe NSNumber, not it's int part, even if it's the only part of it i''m using now. Making this particular variable part of some class doesn't seem like a "right" think to do. Thanks!
Easy answer: you can't. Observing is a software mechanism which fundamentally involves method calls, doing a store (i.e. a machine instruction) into a global variable provides no hook to hang the mechanism on.
The best option is to re-think your design. Think of storing the value in a singleton class and accessing/observing it there.
Hard answer: write your own mutable version of NSNumber (an instance of which is immutable) and have this class implement the key-value observing protocol (this class might just be a wrapper with an NSNumber instance variable). Now store and instance of this class in your global variable and add any observers you like to it.
The usual way to do this kind of thing is by making it a value reachable from some globally-available object, such as NSApp, or its delegate.
CRD is right, "In KVO only the property is observed, not the value" Typically dictionaries or custom objects are KVO but not the leaves (values) themselves (numbers, strings). Facing a similar problem, I finally extended the NSNumber class making it KVO compliant.
Now if you are looking for different approaches to achieve notifications in your app, I would strongly suggest to read this article.

How do I observe the creation/destruction of an object instance?

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).