问题
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???)
回答1:
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.
来源:https://stackoverflow.com/questions/8399715/kvo-differentiating-between-willchangevalueforkey-and-didchangevalueforkey-are