I have a class (call it classA) that contains a property named info (a model class, containing lots of info), wich in turn contains a property named name (a string). I want another class (classB) to receive a KVO notification when the string name changes in classA.
This is what I'm doing now on classB:
[classA addObserver: self forKeyPath: #"info.name" options: 0 context: nil];
There are two ways the value name changes on classA: when it is set directly like classA.info.name = ... and when info is set like classA.info = ...
When name is changed directly KVO works perfectly. However, when the info property is set and name changes indirectly, I get this error:
Cannot update for observer <classB> for the key path "info.name" from <classA>, most likely because the value for the key "info" has changed without an appropriate KVO notification being sent. Check the KVO-compliance of the classA class.
What should I change on classA to make this work?
The cause of this issue comes from the fact that you are implementing the setter -setInfo: for info and calling willChangeValueForKey: | didChangeValueForKey: inside it.
I've seen many instances in which it is believed that the will|didChange... calls need to be implemented for the KVO notification to be fired. This is true only when the setter is not explicitly called. When you do call the setter, the KVO mechanism takes care of firing the notification.
Most of the times leaving these calls in the setter is harmless and just causes extra notifications, but as seen in this case, it does cause a problem when updating a keypath -- as opposed to a key.
In short, if you do implement a setter, do not call will|didChangeValueForKey: inside of it.
Options cannot be 0, I think.
Its an old API, back before Apple got diligent about API design, and you have to give it something.
Related
I have some scenarios where a property is changing, but I do not want to notify observers of the change.
Here is what the code looks like:
[self.fontColorWell removeObserver:self.toolController forKeyPath:#"color"];
[self.fontColorWell setColor:[self.toolController valueForKey:[self fontColorKeyPath]]];
Unfortunately, when the second line executes, the observer (self.toolController) on self.fontColorWell still gets notified.
I'm pretty sure I know why this is happening, because KVO works by the runtime subclassing the observed class, and overridding the observed property to add didChange and willChange messages. (https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html)
If I set a breakpoint at the second line, I can see clearly the class type of self.fontColorWell: NSKVONotifying_NSColorWell. So when -setColor is called, it's still called on the runtime subclass, which has the overridden setter that notifies observers.
But I figured that because I called removeObserver right before, shouldn't the observer be removed from the KVO lookup table, and thus even though the KVO object notifies observers, my self.toolController shouldn't be notified?
I know 100% that when -setColor is called, self.toolController immediately receives the -observeValueForKeyPath... message. I also am positive that all of there are no extra instances of my objects hanging around
Am I just not allowed to do what I'm trying to do? Or am I going about this the wrong way?
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.
I would like to determine if an attribute changed in core data and update my UI. The attribute may change as the result of a background fetch running in a managed object context of type NSPrivateQueueConcurrencyType.
I added a listener: [myCoreDataEntity addObserver:self forKeyPath:myCoreDataAttribute options:NSKeyValueObservingOptionNew context:nil]
But the event never fires. Any idea why? The object is changed in another context - could this be the reason? (When a save: is done on the parent context, nothing still fires).
I can use manual KVO, but since the object has not been saved yet to the parent context, refreshing the UI does not work since it is point at the context in NSMainQueueConcurrencyType when the object was changed in a context associated with NSPrivateQueueConcurrencyType
You're not getting notifications because you're observing the wrong object. The NSEntityDescription never changes at run time. It's a representation of the way the entity was defined in the data model. Instances of NSManagedObject that use the entity description can and do change at run time, though. If you want to know whether an attribute changed on a specific managed object, you need to observe that specific object.
If you need to get notified any time any managed object changes the value for that attribute, your best option is to write a custom setter for that attribute and handle it there. You might also find NSManagedObjectContextObjectsDidChangeNotification useful, but that will fire for any attribute change.
Assuming that myCoreDataEntity in your example is a managed object, the issue is that automatic external change notifications are [disabled by Core Data for managed objects] for modeled properties1:
NSManagedObject disables automatic key-value observing (KVO) change notifications for modeled properties, and the primitive accessor methods do not invoke the access and change notification methods. For unmodeled properties, on OS X v10.4 Core Data also disables automatic KVO; on OS X v10.5 and later, Core Data adopts to NSObject’s behavior.
You can turn them on for specific properties, or for all properties in your managed object subclass or in a category on the managed object subclass:
Single property:
- (BOOL) automaticallyNotifiesObserversFoMyCoreDataAttribute {
return YES;
}
All properties (Not recommended):
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
return YES;
}
The reason it does not send out automatic change notifications is primarily performance. Automatic change notifications do add some overhead, though on recent hardware it's fairly minor, even when working with thousands of objects. As always, profile to see what works for you.
So far I have been using NSNotificationCenter with the method postNotification:aString object:anyObjectOfInterestForTheReceiver. But recently I read in the documentation that the object field should only be passed self. Is there any terrible side effect I am unaware of that should convince me to only pass self in the future, or is it OK to pass any object?
Thank you!
You can pass any object as the object of a notification, but the convention is that the object is the "thing that is doing the notifying" (and you put other relevant state in the userInfo dictionary).
The reason why it's mostly self is because usually the object doing the notifying usually wants to reference itself. That way, for example, if you had many Foo objects, and one of them completed a task and sent a notification, anyone observing the notification could just look at object to see which Foo was the one in question. The observer can also choose to observe only notifications from a particular Foo when you follow this scheme.
It's also reasonable (though less common) to use something besides "self" when posting a notification-- let's say you're sending a notification "on behalf of" another object. For example, you could be a singleton controller object that completes a Bar task, and you could send the notification with a reference to the particular Bar as the object. That makes more sense than using the singleton as the object, since there'd be no interesting variance there.
Again, this is a (useful) convention only. When you make up your own notification, you get to define the "contract" of the notification, which is the name, what kind of object is used as the object, and what's in the userInfo.
Yes, there is a side effect I can think of. Let me explain it.
The method you talk about was actually defined in NSNotification.h as below:
(void)postNotificationName:(NSString *)notificationName object:(id)notificationSender
The first parameter notificationName stands for the name of the notification.
The second parameter notificationSender stands for the object posting the notification.
Yes, any object can be a notification sender, even nil can be.
In terms of observering the notification(to became a observer of a specific notification), we got to know another method defined in NSNotification:
(void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
As you can see, the last parameter is notificationSender(the object whose notifications the observer wants to receive).
So right now, the side effect is apparently. Let me explain it in detail. There are three controllers A, B, C.for example. The controller A post a notification helloEveryone. The controller B also post a notification helloEveryone.
In C controller, if you place a statement like below:
[[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(helloEveryOne) name:#"helloEveryone" object:nil]
Then you will receive two helloEveryone from controller A and controller B.
if you place a statement like this:
[[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(helloEveryOne) name:#"helloEveryone" object:controllerA]
Then you will receive only one helloEveryone from controller A.
The self surely is not a nil notification sender as long as it can post a notification, but use another object as a notification sender, may be it is a nil object. And the observer's behave is different in terms of that whether notification sender is nil or not.
It is crucial if the events really have sender.
When you register an observer, you can specify the sender that you want to observe. If you are letting other people using your message, so it is crucial to pass sender correctly or the observer that bound to specific sender will not get your message.
I saw these lines in a demo project, but I couldn't understand why it did that.
[self willChangeValueForKey:#"names"];
[self didChangeValueForKey:#"names"];
It called didChangeValueForKey immediately after willChangeeValueForKey.
Does it make any sense?
Furthermore, when should be the right time to call this two methods?
Thanks a lot!! :)
This is, in fact, an anti-pattern. You should not call -willChangeValueForKey: followed by -didChangeValueForKey: without any intervening actual property change. In some cases, doing so can mask KVO problems elsewhere in your code and force observers to update their state related to the property in question. Ultimately, however, you (or the author of the example you cite) should fix the rest of the code so that this anti-pattern is unnecessary.
The correct usage of -will|didChangeValueForKey: is when you are modifying a property without using KVC-compliant accessors/setters such that the KVO mechanism would not notice the change. For a contrived example, consider modifying the backing instance variable for an attribute directly:
#interface Foo
{
int bar;
}
#end
#implementation Foo
- (void)someMethod
{
bar = 10;
}
#end
KVO observers that had registered for notification of changes in the bar property would not recieve notification of the change to bar in -someMethod. To make the KVO machinery work, you could modify -someMethod:
- (void)someMethod
{
[self willChangeValueForKey:#"bar"];
bar = 10;
[self didChangeValueForKey:#"bar"];
}
Of course, it would be better to use a #property declaration and to use KVC-compliant accessors/setters (either manually coded or #synthesized), but this is a contrived example.
KVO will operate correctly with custom setters for properties; this has always been the case for NSObject-derived classes. The runtime machinery looks for an invocation of the relevant setter method, and implicitly calls "willChangeValueForKey" prior to executing the setter, then implicitly calls "didChangeValueForKey" after the setter completes.
You can disable this automatic behavior if you wish to have more fine-grained control over KVO notifications. As mentioned above, readonly properties whose value you change by modifying the backing ivar, or whose values are derived by calculation, are places where you would use the manual notifications (although there is a mechanism, keyPathsAffectingValueFor, where you can tell the runtime that the value of a property is dependent on the change of another property, and it will send the change notification as appropriate.) To disable the automatic behavior on a per-property basis, you put in a class method + (BOOL) automaticallyNotifiesObserversOf and return NO.
I often disable automatic KVO notifications, because I have found that a KVO notification is generated when invoking a setter, even if the value of the property is being set to the same as its current value (e.g. no change). I wish to suppress the pointless notification for efficiency's sake:
+ (BOOL)automaticallyNotifiesObserversOfMyProperty
{
return NO;
}
- (void)setMyProperty:(NSInteger)myProperty
{
if(_myProperty != myProperty)
{
[self willChangeValueForKey:#"myProperty"];
_myProperty = myProperty;
[self didChangeValueForKey:#"myProperty"];
}
}
A good discussion can be found in the NSKeyValueObserving.h header, that you can navigate to by CMD+clicking on the method names "willChangeValueForKey" and "didChangeValueForKey" in XCode.
Those have to do with manually controlling key value observing. Normally the system takes care of it but these allow you some control. Look at this documentation to understand when and how to use them here.
Agree with Barry. I just meet the same problem. Here is a case of using those two methods.
I declared a readonly property. So I can't use the property's accessor to change the value.
#property (nonatomic, readonly) BOOL var;
When I want to change the "var", I need to call these two methods manually. Otherwise, observers won't get notified.
self willChangeValueForKey:#"var"];
var = YES;
[self didChangeValueForKey:#"var"];
If you want to do stuff just before the value gets changed, use willChangeValueForKey.
If you want to do stuff just after the value gets changed, use didChangeValueForKey.
Edit: ignore me, was reading too fast - Barry is right :-)
Be really careful when overriding didChangeValueForKey:. The best thing is not to do it at all. But if you do, make sure you call super, otherwise you will have a memory leak as demonstrated here: https://github.com/jfahrenkrug/KVOMemoryLeak
if you rewrite property getter methods, please use it.
#property (assign, nonatomic, getter=isLogined) BOOL logined;
Posting this in July 2013, and it no longer seems to be necessary to call will/didChangeValueForKey. It seems to be taken care of automatically, even if you have a custom setter.