Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KVO differentiating between willChangeValueForKey and didChangeValueForKey - are both necessary?

In line with Apple's own recommendations, when setting KVC/KVO compliant accessors manually, one should include BOTH KVO methods willChange and didChange. This is what I have done in all my manual accessor methods.

However, observeValueForKeyPath:ofObject:change:context gets called for each half of the KVC methods (will and did) with exactly the same dictionary contents.

When registering an observer using the option: NSKeyValueObservingOptionPrior the observer still gets called twice - once for each half - and, again, with identically the same dictionary contents, save only the difference that the key 'notificationIsPrior' is included in the dictionary.

Now, when KVO is used to alter 'CPU-expensive' attributes - like changing a colour or redrawing a large and elaborate design, it makes sense only to act on the 'didChange' and ignore (or at least separate out) the 'willChange'. In the past, I have achieved this by converting the key string into an enum list element that returns a left-shifted '1' and used this digit to set a flag in a 32 or 64 bit integer on receipt of the first call and when the flag is reset on the second, I execute the CPU-intensive operation(s).

However, it strikes me that this is a non-trivial overhead to implement for every case. Does anyone have any other 'preferred' way of differentiating between the callback for 'willChange' and that for 'didChange' without allowing the same processing to be done twice?

I have scoured Apple's own documentation and this help group copiously for alteranatives but Apple's own doc doesn't actually go in to much detail on the subject and several people in this group have also wrestled with a similiar concern. In neither instance has a definitive solution been offered. If anyone knows of a better way - other than dodging the 'willChange' using alternating flags - I'd be very grateful. (Why couldn't Apple just include a 'phase' key in the change dictionary???)

like image 414
VectorVictor Avatar asked Dec 06 '11 12:12

VectorVictor


People also ask

How does KVO work?

KVO, which stands for Key-Value Observing, is one of the techniques for observing the program state changes available in Objective-C and Swift. The concept is simple: when we have an object with some instance variables, KVO allows other objects to establish surveillance on changes for any of those instance variables.

What is Key-Value Coding and Key-Value Observing?

KVO and KVC or Key-Value Observing and Key-Value Coding are mechanisms originally built and provided by Objective-C that allows us to locate and interact with the underlying properties of a class that inherits NSObject at runtime.

What is KVO in Objective-C?

KVO allows you to register as an observer of a given object and receive notification when specific properties on that object are changed. It's an incredibly powerful capability, and it is built into Objective-C at its very core.


1 Answers

I think this is what you were getting at in the comments, but for the benefit of future visitors:

If you want to tell whether a callback is "before" or "after" you can look for the NSKeyValueChangeNotificationIsPriorKey key in the change dictionary. If it's a prior notification, this key will be equal to [NSNumber numberWithBool: YES] (incidentally the dictionary will also not contain a value for the NSKeyValueChangeNewKey) The presence/value of NSKeyValueChangeNotificationIsPriorKey is authoritative, so if you're seeing it when you're not expecting to, you might be getting double callbacks.

If you're getting double callbacks it may be, as it sounds like it was in VectorVictors case, that the runtime is firing them AND you're firing them. If you plan to call will/didChangeValueForKey: to manage your KVO notifications manually, (and you don't want double notifications,) you should implement the following class method:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {

    BOOL automatic = NO;
    if ([theKey isEqualToString:@"propertyYourePlanningToManageYourself"]) {
        automatic = NO;
    } else {
        automatic=[super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}

This is described in detail in Apple's Key-Value Observing Programming Guide.

like image 53
ipmcc Avatar answered Oct 06 '22 00:10

ipmcc