【问题标题】:Help with Key-Value-Observing帮助键值观察
【发布时间】:2009-10-02 16:05:32
【问题描述】:

我需要一些关于 KVO 的帮助,我已经完成了一半。我想做的是在树控制器中的某些内容发生变化时触发一个方法。

所以我正在使用此代码注册为 KVO。

[theObject addObserver: self
            forKeyPath: @"myKeyPath"
               options: NSKeyValueObservingOptionNew
               context: NULL];

但是当我观察到的 Key Path 发生变化时,如何触发方法呢?

一个额外的问题,当我将自己添加为观察者时,我希望关键路径成为我的核心数据模型中的一个属性,我这样做正确吗?

【问题讨论】:

    标签: objective-c cocoa core-data cocoa-bindings key-value-observing


    【解决方案1】:

    覆盖observeValueForKeyPath:ofObject:change:context: 以调度您希望调用的方法。

    @interface Foo : NSObject {
        NSDictionary *dispatch;
        ...
    }
    @end
    @implementation Foo
    -(id)init {
        if (self = [super init]) {
            dispatch = [[NSDictionary dictionaryWithObjectsAndKeys:NSStringFromSelector(@selector(somethingHappenedTo:with:)),@"myKeyPath",...,nil] retain];
            ...
        }
    }
    ...
    - (void)observeValueForKeyPath:(NSString *)keyPath
                ofObject:(id)object
                change:(NSDictionary *)change
                context:(void *)context
    {
        SEL msg = NSSelectorFromString([dispatch objectForKey:keyPath]);
        if (msg) {
            [self performSelector:msg withObject:object withObject:keyPath];
        }
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
    ...
    

    详见“Receiving Notification of a Change”。

    【讨论】:

    • "NSDictionary 调度;"应该是“NSDictionary *dispatch;”,否则很好。
    • 哦。固定的。还将“performSelector”更改为“performSelector:withObject:withObject”,以获得更普遍有用的方法。
    • 一个额外的问题,当我将自己添加为观察者时,我希望关键路径成为我的核心数据模型中的一个属性,我是否正确地做到了?
    • @Joshua:对于 KVO 的常见应用,属性名称听起来像是正确的密钥路径。但是,如果您正在做一些不寻常的事情...
    • 我明白了,我没有收到任何错误,但选择器不起作用。这是代码的图片……snapplr.com/0q5f
    【解决方案2】:

    我建议您查看 Google Toolbox For Mac 的 GTMNSObject+KeyValueObserving.h 类别,或者至少查看激发它灵感的 Michael Ash 的博客 post。基本上,正确地进行手动 KVO 是非常微妙的,API 建议的模式并不理想。最好在 API 上放置一个其他层(如 GTMNSObject+KeyValueObserving),这会使事情更像NSNotification API 并隐藏一些细微错误的来源。

    使用 GTMNSObject+KeyValueObserving,你会这样做

    [theObject gtm_addObserver:self
                    forKeyPath:@"myKeyPath"
                      selector:@selector(myCallbackSelector:)
                      userInfo:nil
                       options:NSKeyValueObservingOptionNew];
    

    @"myKeyPath" 的值随GTMKeyValueChangeNotification 类型的参数发生变化时,您的-myCallbackSelector: 将被调用,该参数封装了您可能需要的所有相关信息。

    这样,您不必在observeValueForKeyPath:ofObject:change:context 中有一个大的调度表(实际上是由类别为您维护的)或者不必担心使用context 指针的正确方法来避免与超/子类等冲突。

    【讨论】:

    • 看起来很有趣,我在哪里下载代码/框架?
    • code.google.com/p/google-toolbox-for-mac GTM 就像 boost C++ 库一样,是一个随心所欲的架构。他们建议您将所需的文件/类直接包含到您自己的项目中。只有几个常见的包含。
    【解决方案3】:

    你应该实现它,它会在 keypath 改变时被调用:

     (void)observeValueForKeyPath:(NSString *)keyPath
                         ofObject:(id)object
                           change:(NSDictionary *)change
                          context:(void *)context;
    

    更多信息here

    【讨论】:

      【解决方案4】:

      (这是我在这里学到的技术:http://www.bit-101.com/blog/?p=1999

      您可以在“上下文”中传递方法,例如

      [theObject addObserver:self 
                  forKeyPath:@"myKeyPath"
                     options:NSKeyValueObservingOptionNew
                     context:@selector(doSomething)];
      

      ..然后在 observeValueForKeyPath 方法中,将其强制转换为 SEL 选择器类型,然后执行。

      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
      {
          SEL selector = (SEL)context;
          [self performSelector:selector];
      }
      

      如果您想将数据传递给 doSomething 方法,您可以使用 'change' 字典中的 'new' 键,如下所示:

      [theObject addObserver:self 
                    forKeyPath:@"myKeyPath"
                       options:NSKeyValueObservingOptionNew
                       context:@selector(doSomething:)]; // note the colon
      
      - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
          {
              SEL selector = (SEL)context;
      
              // send the new value of observed keyPath to the method
              [self performSelector:selector withObject:[change valueForKey:@"new"]]; 
          }
      
      
      -(void)doSomething:(NSString *)newString // assuming it's a string
      {
            label.text = newString;
      }
      

      【讨论】:

        猜你喜欢
        • 2011-06-09
        • 2012-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        相关资源
        最近更新 更多