31

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!! :)

Frost
  • 3,786
  • 5
  • 23
  • 29

8 Answers8

53

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.

Barry Wark
  • 107,306
  • 24
  • 181
  • 206
  • I ran across the same issue. Here's a bit more on KVO from Apple http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i – Dan Nov 21 '12 at 23:11
  • Can you tell me exactly what KVO problems elsewhere in my code would be masked by call willChangeValue immediately followed by didChangeValue? And when you talk of forcing KVO observers to update their state, do you mean that a KVO change notification will be emitted when the value actually hasn't changed, if one follows this anti-pattern? – Kartick Vaddadi Mar 20 '18 at 03:33
13

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.

KPM
  • 10,558
  • 3
  • 45
  • 66
Kevin Draz
  • 201
  • 2
  • 3
1

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.

regulus6633
  • 18,848
  • 5
  • 41
  • 49
1

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"];
yibuyiqu
  • 728
  • 1
  • 7
  • 20
  • var=YES won't compile. Do you mean self.var=YES (which also won't compile with readonly)? If you mean _var=YES, that will compile, but since you aren't using the generated setter, i'm not surprised will/didChange aren't called. I'd use self.var = YES without readonly. – Amir Memon Jan 31 '14 at 20:03
  • Yes. I meant _var=YES. My case is how to use Will/didChange call to notify manually. – yibuyiqu Feb 11 '14 at 00:09
0

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

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
0
  • 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 :-)

DenverCoder9
  • 3,635
  • 3
  • 31
  • 57
0

if you rewrite property getter methods, please use it.

@property (assign, nonatomic, getter=isLogined) BOOL logined;
-2

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.

Amir Memon
  • 416
  • 3
  • 10