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
Related
Short version:
What use is -removeObserver:forKeyPath:?
Why not always use -removeObserver:forKeyPath:context:?
Long version
While working on a Cocoa program, I discovered that using -removeObserver:forKeyPath: could (but would not always) lead to an error like:
Cannot remove an observer <ObservedClass 0x1001301d0> for the key path "exampleKeyPath" from <__NSCFConstantString 0x100009138> because it is not registered as an observer.
while using -removeObserver:forKeyPath:context: instead would work just fine.
Since it is required that a context be specified when setting up observation (with -observeValueForKeyPath:ofObject:change:context:), I'm puzzled at why the context:-less removal method exists.
Based on my reading of the NSKeyValueObserving Protocol, I supposed that the removal might apply to the specified observer and specified key path in all contexts, but the failure of -removeObserver:forKeyPath: (with no context) to work as a replacement for -removeObserver:forKeyPath:context: (with a context of NULL) seems to shoot down that idea.
So: why might I have that error? What does -removeObserver:forKeyPath: do with contexts? How's it differ from its context:-equipped younger sibling?
Code example
Problematic code:
-(void) invalidate {
[(id)observedObject removeObserver:self
forKeyPath:#"exampleKeyPath"];
}
Non-Problematic code:
-(void) invalidate {
[(id)observedObject removeObserver:self
forKeyPath:#"exampleKeyPath"
context:NULL];
}
Short version: -removeObserver:forKeyPath:context: was only introduced in 10.7, hence both.
Long version: Why might you have the error? Looks like a bug, either in your code or the system (I've never seen the error and use the shorter version a lot). The descriptions of the two methods do not suggest there should be any difference. If nobody else comes up with an explanation, and you can't find anything in your code, then report a bug to Apple.
The documentation has an excellent discussion as to the use of the new method:
Examining the value in context you are able to determine precisely which addObserver:forKeyPath:options:context: invocation was used to create the observation relationship. When the same observer is registered for the same key-path multiple times, but with different context pointers, an application can determine specifically which object to stop observing
It's just a way to be more specific about just which binding you want to remove from the object. For example, I might bind to a keypath twice, but with the memory locations of different static variables, a little like how dispatch_once() works. The context-free subscription method was the only way of binding to an object until 10.7 rolled around and filled in that gap.
As for your KVO troubles, the problem can occur in many different cases. The most common being that you've subscribed on one thread, then very soon after, removed a subscription from a different thread. Occasionally it can occur because the object you tried to observe was just about to deallocate, meaning you would subscribe to some bogus memory location that happened to fill what you needed to, then removing the subscription from this garbage pointer would be impossible. Either way, make sure to monitor the methods you're utilizing bindings in, as they can be a little unstable if used in the wrong way.
thanks for viewing this post, it'd be great if you guys can help me out. I've been doing some objective-c and learned about the objective-c way of memory management, like making sure to call release whenever I own the object, when to call autorelease, etc. I also do not want to use ARC or the newly introduced GC because I like to manage my own memory, I plan to advance later on into iOS development, and I know it's a good practice to manage my own memory. But there's still one small detail that I seem to have hit a brick wall in. It has to do with sending objects the -retain message. I learned that sending the -retain message increments the reference count by 1. But would this be an appropriate time to send -retain? :
- (void) setName : (NSString* ) theName
{
// name is an instance variable of type NSString
[theName retain]; // Must release this
name = [theName copy]; // Must release this in dealloc
[theName release]; // decrement the reference count because of retain
}
Should I call retain here so that I own the argument temporarily and ensure it doesnt'
get released somehow before I get to use it?
Any help would be appreciated! Thanks!
No. You the object supplied as an argument to the method will generally be around until your method returns. You don't need the retain messages there. You copy the string here to keep it around after the method returns.
This is documented in Apple's Documentation on this page in the "Avoid Causing Deallocation of Objects You’re Using" Section. Specifically:
Cocoa’s ownership policy specifies that received objects should
typically remain valid throughout the scope of the calling method. It
should also be possible to return a received object from the current
scope without fear of it being released. It should not matter to your
application that the getter method of an object returns a cached
instance variable or a computed value. What matters is that the object
remains valid for the time you need it.
As an aside you really should consider using ARC. Its not good practise to manage your own memory. No matter how good one can be at managing their own memory the LLVM compiler is still better. Managing your own memory will lead to hard to troubleshoot issues caused only by yourself. It is an extra level of cognitive load that you really don't have to deal with and, when you finally let manual memory management go, you will breathe a sigh of relief at all the mental overhead you didn't even know was there.
NSProxy seems to work very well as stand-in objects for those that don't yet exist. For example.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
The above code will transparently pass any method invocation to the target that the proxy represents. However, it doesn't seem to handle KVO observations and notifications on the target. I tried to use a NSProxy subclass as standing for objects to be passed to NSTableView, but I'm getting the following error.
Cannot update for observer <NSAutounbinderObservance 0x105889dd0> for
the key path "objectValue.status" from <NSTableCellView 0x105886a80>,
most likely because the value for the key "objectValue" has changed
without an appropriate KVO notification being sent. Check the
KVO-compliance of the NSTableCellView class.
Is there a way to make transparent NSProxy that is KVO compliant?
The crux of the issue is that the guts of Key-Value Observing lives in NSObject, and NSProxy doesn't inherit from NSObject. I'm reasonably confident that any approach will require the NSProxy object to keep its own list of observances (i.e. what outside folks are hoping to observe about it.) This alone would add considerable weight to your NSProxy implementation.
Observe the target
It looks like you've already tried having observers of the proxy actually observe the real object -- in other words, if the target were always populated, and you simply forwarded all invocations to the target, you would also be forwarding addObserver:... and removeObserver:... calls. The problem with this is that you started out by saying:
NSProxy seems to work very well as stand-in objects for those that
don't yet exist
For completeness, I'll describe some of the guts of this approach and why it can't work (at least for the general case):
In order for this to work, your NSProxy subclass would have to collect invocations of the registration methods that were called before the target was set, and then pass them through to the target when it gets set. This quickly gets hairy when you consider that you must also process removals; you wouldn't want to add an observation that was subsequently removed (since the observing object could have been dealloc'ed). You also probably don't want your method of tracking observations to retain any of the observers, lest this create unintended retain cycles. I see the following possible transitions in target value that would need to be handled
Target was nil on init, becomes non-nil later
Target was set non-nil, becomes nil later
Target was set non-nil, then changes to another non-nil value
Target was nil (not on init), becomes non-nil later
...and we run into problems right away in case #1. We would probably be all right here if the KVO observer only observed objectValue (since that will always be your proxy), but say an observer has observed a keyPath that goes through your proxy/real-object, say objectValue.status. This means that the KVO machinery will have called valueForKey: objectValue on the target of the observation and gotten your proxy back, then it will call valueForKey: status on your proxy and will have gotten nil back. When the target becomes non-nil, KVO will have considered that value to have changed out from under it (i.e. not KVO compliant) and you'll get that error message you quoted. If you had a way to temporarily force the target to return nil for status, you could turn that behavior on, call -[target willChangeValueForKey: status], turn the behavior off, then call -[target didChangeValueForKey: status]. Anyway, we can stop here at case #1 because they have the same pitfalls:
nil won't do anything if you call willChangeValueForKey: on it (i.e. the KVO machinery will never know to update its internal state during a transition to or from nil)
forcing any target object to have a mechanism whereby it will temporarily lie and return nil from valueForKey: for all keys seems like a pretty onerous requirement, when the stated desire was a "transparent proxy".
what does it even mean to call setValue:forKey: on a proxy with a nil target? do we keep those values around? waiting for the real target? do we throw? Huge open issue.
One possible modification to this approach would be to use a surrogate target when the real target is nil, perhaps an empty NSMutableDictionary, and forward KVC/KVO invocations to the surrogate. This would solve the problem of not being able to meaningfully call willChangeValueForKey: on nil. All that said, assuming you've maintained your list of observations, I'm not optimistic that KVO will tolerate the following sequence that would be involved with setting the target here in case #1:
outside observer calls -[proxy addObserver:...], proxy forwards to dictionary surrogate
proxy calls -[surrogate willChangeValueForKey:] because target is being set
proxy calls -[surrogate removeObserver:...] on surrogate
proxy calls -[newTarget addObserver:...] on new target
proxy calls -[newTarget didChangeValueForKey:] to balance call #2
It's not clear to me that this won't also lead to the same error. This whole approach is really shaping up to be a hot mess, isn't it?
I did have a couple alternate ideas, but #1 is fairly trivial and #2 and #3 aren't simple enough or confidence-inspiring enough to make me want to burn the time to code them up. But, for posterity, how about:
1. Use NSObjectController for your proxy
Sure, it gums up your keyPaths with an extra key to get through the controller, but this is sort of NSObjectController's whole reason for being, right? It can have nil content, and will handle all the observation set up and tear-down. It doesn't achieve the goal of a transparent, invocation forwarding proxy, but for example, if the goal is to have a stand-in for some asynchronously generated object, it would probably be fairly straightforward to have the asynchronous generation operation deliver the final object to the controller. This is probably the lowest-effort approach, but doesn't really address the 'transparent' requirement.
2. Use an NSObject subclass for your proxy
NSProxy's primary feature isn't that it has some magic in it -- the primary feature is that it doesn't have (all) the NSObject implementation in it. If you're willing to go to the effort to override all NSObject behaviors that you don't want, and shunt them back around into your forwarding mechanism, you can end up with the same net value provided by NSProxy but with the KVO support mechanism left in place. From there, it's a matter of your proxy watching all the same key paths on the target that were observed on it, and then rebroadcasting willChange... and didChange... notifications from the target so that outside observers see them as coming from your proxy.
...and now for something really crazy:
3. (Ab)Use the runtime to bring the NSObject KVC/KVO behavior into your NSProxy subclass
You can use the runtime to get the method implementations related to KVC and KVO from NSObject (i.e. class_getMethodImplementation([NSObject class], #selector(addObserver:...))), and then you can add those methods (i.e. class_addMethod([MyProxy class], #selector(addObserver:...), imp, types)) to your proxy subclass.
This will likely lead to a guess-and-check process of figuring out all the private/internal methods on NSObject that the public KVO methods call, and then adding those to the list of methods that you wholesale over. It seems logical to assume that the internal data structures that maintain KVO observances would not be maintained in ivars of NSObject (NSObject.h indicates no ivars -- not that that means anything these days) since that would mean that every NSObject instance would pay the space price. Also, I see a lot of C functions in stack traces of KVO notifications. I think you could probably get to a point where you had brought in enough functionality for the NSProxy to be a first-class participant in KVO. From that point forward, this solution looks like the NSObject based solution; you observe the target and rebroadcast the notifications as if they came from you, additionally faking up willChange/didChange notifications around any changes to the target. You might even be able to automate some of this in your invocation forwarding mechanism by setting a flag when you enter any of the KVO public API calls, and then attempting to bring over all methods called on you until you clear the flag when the public API call returns -- the hitch there would be trying to guarantee that bringing over those methods didn't otherwise ruin the transparency of your proxy.
Where I suspect this will fall down is in the mechanism whereby KVO creates dynamic subclasses of your class at runtime. The details of that mechanism are opaque, and would probably lead to another long train of figuring out private/internal methods to bring in from NSObject. In the end, this approach is also completely fragile, lest any of the internal implementation details change.
...in conclusion
In the abstract, the problem boils down to the fact that KVO expects a coherent, knowable, consistently updated (via notifications) state across it's key space. (Add "mutable" to that list if you want to support -setValue:forKey: or editable bindings.) Barring dirty tricks, being first class participants means being NSObjects. If one of those steps in the chain implements it's functionality by calling through to some other internal state, that's its prerogative, but it'll be responsible for fulfilling all its obligations for KVO compliance.
For that reason, I posit that if any of these solutions are worth the effort, I'd put my money on the "using an NSObject as the proxy and not NSProxy." So to get to the exact nature of your question, there may be a way to make an NSProxy subclass that is KVO compliant, but it hardly seems like it would worth it.
I don't have the exact same use case (no bindings) of OP but mine was similar: I am creating an NSProxy subclass that presents itself as another object that is actually loaded from a server. During the load, other objects can subscribe to the proxy and the proxy will forward the KVO as soon as the object arrives.
There is a simple NSArray property in the proxy that records all observers. Until the real object is loaded, the proxy returns nil in valueForKey:. When the realObject arrives, the proxy calls addObserver:forKeyPath:options:context: on the real object and then, through the magic of the runtime, walks through all properties of realObject and does this:
id old = object_getIvar(realObject, backingVar);
object_setIvar(realObject, backingVar, nil);
[realObject willChangeValueForKey:propertyName];
object_setIvar(realObject, backingVar, old);
[realObject didChangeValueForKey:propertyName];
This seems to work, at least I haven't gotten any KVO compliance errors yet. It does make sense though, first all properties are nil and then they change from nil to the actual value. It is all like ipmcc said in his first statement above, so this post is just a confirmation! Note that the second surrogate that he proposed actually isn't needed, you just have to keep track of observers yourself.
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
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.