【问题标题】:when to use "willChangeValueForKey" and "didChangeValueForKey"?何时使用“willChangeValueForKey”和“didChangeValueForKey”?
【发布时间】:2010-06-10 20:28:49
【问题描述】:

我在一个演示项目中看到了这些行,但我不明白为什么会这样。

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

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

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

【问题讨论】:

    标签: objective-c macos


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

    • 我遇到了同样的问题。这是来自 Apple developer.apple.com/library/ios/#documentation/cocoa/conceptual/… 的更多关于 KVO 的内容@
    • 你能告诉我代码中其他地方的哪些 KVO 问题会被调用 willChangeValue 并紧跟 didChangeValue 所掩盖吗?当你谈到强制 KVO 观察者更新他们的状态时,你的意思是如果遵循这种反模式,当值实际上没有改变时会发出 KVO 更改通知?
    【解决方案2】:

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

    如果您希望对 KVO 通知进行更精细的控制,可以禁用此自动行为。如上所述,通过修改支持 ivar 更改其值或通过计算派生其值的只读属性是您将使用手动通知的地方(尽管有一种机制 keyPathsAffectingValueFor,您可以在其中告诉运行时一个属性的值取决于另一个属性的变化,它会在适当的时候发送变化通知。)要在每个属性的基础上禁用自动行为,你输入一个类方法 + (BOOL) automaticallyNotifiesObserversOf 并返回 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”导航到该标头。

    【讨论】:

    • 你的意思是+keyPathsForValuesAffecting<Key>而不是keyPathsAffectingValueFor
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      同意巴里。我只是遇到同样的问题。这是使用这两种方法的案例。 我声明了一个只读属性。所以我不能使用属性的访问器来改变值。

      @property (nonatomic, readonly) BOOL var;
      

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

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

      【讨论】:

      • var=YES 不会编译。你的意思是 self.var=YES (它也不会用 readonly 编译)?如果您的意思是 _var=YES,那将编译,但是由于您没有使用生成的设置器,所以我不会感到惊讶 will/didChange 不会被调用。我会使用 self.var = YES 而不使用 readonly。
      • 是的。我的意思是_var=YES。我的案例是如何使用 Will/didChange 调用手动通知。
      【解决方案5】:
      • 如果您想在值更改之前执行操作,请使用 willChangeValueForKey。
      • 如果您想在值更改后立即执行操作,请使用 didChangeValueForKey。

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

            • will/didChangeValueForKey 仍然需要在值更改时通知观察者不使用设置器。
            猜你喜欢
            • 1970-01-01
            • 2015-09-08
            • 1970-01-01
            • 1970-01-01
            • 2010-12-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-20
            • 2014-11-29
            相关资源
            最近更新 更多