何时使用“willChangeValueForKey”和“didChangeValueForKey”?

时间:2010-06-10 20:28:49

标签: objective-c macos

我在演示项目中看到了这些行,但我无法理解为什么会这样做。

[self willChangeValueForKey:@"names"];
[self didChangeValueForKey:@"names"];

在willChangeeValueForKey之后立即调用didChangeValueForKey。 它有意义吗?

此外,什么时候应该适时调用这两种方法? 非常感谢!! :)

8 个答案:

答案 0 :(得分:49)

事实上,这是一种反模式。您不应在-willChangeValueForKey:之后调用-didChangeValueForKey:,而不会在没有任何实际属性更改的情况下调用-will|didChangeValueForKey:。在某些情况下,这样做可能会掩盖代码中其他地方的KVO问题,并迫使观察者更新与相关属性相关的状态。但是,最终,您(或您引用的示例的作者)应该修复其余的代码,以便不需要这种反模式。

@interface Foo { int bar; } @end @implementation Foo - (void)someMethod { bar = 10; } @end 的正确用法是在不使用符合KVC的访问者/设置者修改属性时,KVO机制不会注意到更改。对于一个人为的例子,考虑直接修改属性的后备实例变量:

bar

已注册bar属性变更通知的KVO观察员不会在-someMethod中收到-someMethod更改通知。要使KVO机器正常工作,您可以修改- (void)someMethod { [self willChangeValueForKey:@"bar"]; bar = 10; [self didChangeValueForKey:@"bar"]; }

@property

当然,最好使用@synthesized声明并使用符合KVC标准的访问者/设置者(手动编码或{{1}}),但这是一个人为的例子。

答案 1 :(得分:11)

KVO将使用自定义的属性设置器正常运行;这一直是NSObject派生类的情况。运行时机制查找相关setter方法的调用,并在执行setter之前隐式调用“willChangeValueForKey”,然后在setter完成后隐式调用“didChangeValueForKey”。

如果您希望对KVO通知进行更细粒度的控制,可以禁用此自动行为。如上所述,readonly属性通过修改支持ivar或通过计算得出其值来更改的值是您将使用手动通知的位置(尽管有一个机制,keyPathsAffectingValueFor,您可以在其中告诉运行时属性的值取决于另一个属性的更改,它将根据需要发送更改通知。)要在每个属性的基础上禁用自动行为,您将自定义类方法+(BOOL)自动注释ObserversOf并返回NO

我经常禁用自动KVO通知,因为我发现在调用setter时会生成KVO通知,即使该属性的值设置为与其当前值相同(例如,没有更改)。为了效率,我希望压制毫无意义的通知:

+ (BOOL)automaticallyNotifiesObserversOfMyProperty
{
  return NO;
}

- (void)setMyProperty:(NSInteger)myProperty
{
  if(_myProperty != myProperty)
  {
    [self willChangeValueForKey:@"myProperty"];
    _myProperty = myProperty;
    [self didChangeValueForKey:@"myProperty"];
  }
}

可以在NSKeyValueObserving.h标题中找到一个很好的讨论,你可以通过CMD +导航到XCode中的方法名称“willChangeValueForKey”和“didChangeValueForKey”。

答案 2 :(得分:1)

那些与手动控制键值观察有关。通常情况下,系统负责处理,但这些可以让您进行一些控制。请查看此文档以了解何时以及如何使用它们here

答案 3 :(得分:1)

同意Barry。我只是遇到同样的问题。这是使用这两种方法的情况。 我宣布了一个只读属性。所以我不能使用属性的访问器来更改值。

@property (nonatomic, readonly) BOOL var;

当我想要更改“var”时,我需要手动调用这两个方法。否则,观察员将不会收到通知。

self willChangeValueForKey:@"var"];
var = YES;
[self didChangeValueForKey:@"var"];

答案 4 :(得分:0)

  • 如果您想在更改值之前执行操作,请使用willChangeValueForKey。
  • 如果您想在更改值后立即执行操作,请使用didChangeValueForKey。

编辑:不理我,读得太快 - 巴里是对的: - )

答案 5 :(得分:0)

覆盖didChangeValueForKey:时要非常小心。最好的事情就是不要这样做。但是,如果你这样做,请确保拨打super,否则您将遇到内存泄漏,如下所示:https://github.com/jfahrenkrug/KVOMemoryLeak

答案 6 :(得分:0)

如果您重写属性获取器方法,请使用它。

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

答案 7 :(得分:-2)

在2013年7月发布,似乎不再需要调用will / didChangeValueForKey。即使您有自定义设置器,它似乎也会自动处理。