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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3018242/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 07:00:06  来源:igfitidea点击:

when to use "willChangeValueForKey" and "didChangeValueForKey"?

objective-cmacos

提问by Frost

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?

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

Furthermore, when should be the right time to call this two methods? Thanks a lot!! :)

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

回答by Barry Wark

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.

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

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:

的正确用法-will|didChangeValueForKey:是在不使用符合 KVC 的访问器/设置器的情况下修改属性时,这样 KVO 机制就不会注意到更改。对于人为的示例,请考虑直接修改属性的支持实例变量:

@interface Foo
{
   int bar;
}
@end

@implementation Foo
- (void)someMethod
{
  bar = 10;
}
@end

KVO observers that had registered for notification of changes in the barproperty would not recieve notification of the change to barin -someMethod. To make the KVO machinery work, you could modify -someMethod:

已注册bar属性更改通知的 KVO 观察者将不会收到更改为barin 的通知-someMethod。要使 KVO 机器工作,您可以修改-someMethod

- (void)someMethod
{
  [self willChangeValueForKey:@"bar"];
  bar = 10;
  [self didChangeValueForKey:@"bar"];
}

Of course, it would be better to use a @propertydeclaration and to use KVC-compliant accessors/setters (either manually coded or @synthesized), but this is a contrived example.

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

回答by Kevin Draz

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.

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

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.

如果您希望对 KVO 通知进行更细粒度的控制,则可以禁用此自动行为。如上所述,通过修改后备 ivar 更改其值或通过计算得出其值的只读属性是您将使用手动通知的地方(尽管有一种机制,keyPathsAffectingValueFor,您可以在其中告诉运行时一个属性的值取决于另一个属性的更改,它会根据情况发送更改通知。)要在每个属性的基础上禁用自动行为,请放入一个类方法 + (BOOL) automaticNotifyingObserversOf 并返回 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:

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

+ (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.

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

回答by regulus6633

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.

这些与手动控制键值观察有关。通常系统会处理它,但这些允许您进行一些控制。在此处查看此文档以了解何时以及如何使用它们。

回答by yibuyiqu

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.

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

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

回答by DenverCoder9

  • 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.
  • 如果您想在值更改之前执行某些操作,请使用 willChangeValueForKey。
  • 如果您想在值更改后立即执行操作,请使用 didChangeValueForKey。

Edit: ignore me, was reading too fast - Barry is right :-)

编辑:忽略我,阅读太快了 - 巴里是对的 :-)

回答by Johannes Fahrenkrug

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

覆盖时要非常小心didChangeValueForKey:。最好的办法是根本不做。但是,如果这样做,请确保调用super,否则将出现内存泄漏,如下所示:https: //github.com/jfahrenkrug/KVOMemoryLeak

回答by geekduan

if you rewrite property getter methods, please use it.

如果您重写属性 getter 方法,请使用它。

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

回答by Amir Memon

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.

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