【问题标题】:NSMutableDictionary KVONSMutableDictionary KVO
【发布时间】:2012-03-08 02:04:57
【问题描述】:

我正在尝试使用 KVO 观察字典的变化。

例子:

dictionary = [NSMutableDictionary new];
[dictionary setObject:@"test1" forKey:@"key1"];
[dictionary setObject:@"test2" forKey:@"key2"];    
[dictionary setObject:@"test3" forKey:@"key1"];

我希望能够在每次向字典中添加值时挂钩观察者。删除或替换(即在上述情况下,只要调用任何 setObject 方法)

总之: 我想要一个函数有

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

当我在字典中添加任何新条目、删除任何条目或替换任何条目时调用。

NOT:我不想指定我正在观察哪些键。 (例如,仅在添加 @"key1" 时观察)因为此解决方案无法扩展。

【问题讨论】:

  • 如果你观察字典上的@"@count"键会发生什么?
  • 没有。没用。如果我将 @"@count" 替换为 @"key2" 它可以工作,但这不是一个通用的解决方案。

标签: objective-c key-value-observing


【解决方案1】:

子类化 NSMutableDictionary 有点烦人,因为 NSDictionary 和它的朋友都是类簇。这当然是可行的,如果您必须将对象本身传递给另一组类,那么您可能想要这样做。否则,创建一个具有相同基本 API 并在内部使用 NSMutableDictionary 对象进行存储的复合类可能会更容易。 CocoaWithLove.com 有一篇很好的文章,Ordered Dictionary Subclassing,就是这样做的。

但是,这并不能完全解决您的问题。我建议你从一个子类或装饰器类开始,例如上面的那个,然后显式添加对-(NSArray*)allKeys 的支持,这是NSDictionary 本身的标准访问器。然后,您可以添加支持以传递 allKeys 的更改消息,这将使其可观察。

这可以通过在 -setObject:forKey:-removeObjectForKey: 方法周围添加以下代码来完成。

- (void)setObject:(id)anObject forKey:(id)aKey
{
    BOOL addKey=NO;
    if (![dictionary objectForKey: aKey]) {
        addKey=YES;
        [self willChangeValueForKey: @"allKeys"];   
    }
    [dictionary setObject:anObject forKey:aKey];
    if (addKey)
        [self didChangeValueForKey: @"allKeys"];
}

- (void)removeObjectForKey:(id)aKey
{
    [self willChangeValueForKey: @"allKeys"];
    [dictionary removeObjectForKey:aKey];
    [self didChangeValueForKey: @"allKeys"];
}

这里所做的是,当字典的键发生更改以标记数组中的更改时,我们正在向类添加显式 KVO 通知。

这将负责添加和删除。如果您希望在相同的基础上通知更改,您可以删除 if 语句,并让 allKeys 在 set 或 remove 时通知,如下所示:

- (void)setObject:(id)anObject forKey:(id)aKey
{
    [self willChangeValueForKey: @"allKeys"];   
    [dictionary setObject:anObject forKey:aKey];
    [self didChangeValueForKey: @"allKeys"];
}

然后,在您的代码中,您为该对象上的键 @"allKeys" 放置一个观察者,并且您将在项目更改时收到通知。

【讨论】:

  • 我同意这个结论。谢谢盖吉。我要实现这个。
  • 如果可行,请不要忘记对您认为有帮助的答案进行投票,并将提供您解决方案的答案标记为已回答。
  • 当 NSMutableDictionary 变大时,这些方法不会为每个键再次调用吗??
  • “变大”是一个有趣的描述选择。我在我的代码中显示了两个变体,一个用于注意字典中键的添加和删除,另一个显示对字典进行更改的任何时间(基本上是删除 setObject:forKey: 方法中的 if 语句)。
【解决方案2】:

我通过以这种方式向可变字典“翻译器”添加观察者解决了类似的问题:

[self addObserver:self forKeyPath:@"translator.@count" options:0 context:NULL];

我的应用程序以经典方式管理数据,使用 tableview、控制器和字典作为 KVO 属性“翻译器”。 字典绑定到我的 XIB 中的 NSDictionaryController,并且 tableview 内容绑定到控制器。

这是tableview的连接:

现在,在以下任何一种情况下,我都会发现变化:

  • 添加键值对
  • 删除键值对
  • 更改密钥
  • 更改值

备注:不幸的是,这种方法不适用于 NSMutableArrays。 无法识别更改

【讨论】:

    【解决方案3】:

    你不能继承 NSMutableDictionary 并覆盖各种设置器吗?例如,通过调用 super 覆盖 setObject:forKey:,然后立即调用 addObserver...

    您还可以为 NSMutableDictionary 编写一个包装器,强制自己使用自定义设置器来操作底层 NSMutableDictionary。

    也许我需要更多上下文来了解您的任何限制或可扩展性意图。

    【讨论】:

    • 重写 NSMutableDictionary 是非法的(令我惊讶的是)。调用 [super SetObject: forKey:] 会导致异常说明抽象类。我倾向于组合并重新实现字典可以提供的所有功能(PITA 和非多态)。可伸缩性问题是指您只能观察字典中“key1”的更改,而不能观察字典中的“*”更改。因此,如果向字典中添加了新键,我无法通知观察者。
    • 确实很有趣。到目前为止,我认为装饰器类可能是最好的选择。不过,您不必重新实现所有方法。您可以将字典作为成员并为任何非 kvo 操作调用 [myObject.dictionary nsdictionarymethod] 并重新实现添加、设置、删除方法。
    【解决方案4】:

    我希望这会有所帮助

    - (void)addObserver:(id)observer {
        for (id key in grid)
            [self addObserver:observer
                   forKeyPath:[key description]
                      options:0
                      context:key];
    }
    

    【讨论】:

      【解决方案5】:

      我认为另一种方法是使用以下覆盖,以防您观察 NSMutableDictionary “allRecentCurrencyData”,其值取决于recentBrazilReals、recentEuEuro、recentUkPounds、recentJapanYen,观察者将被调用,但缺点是您需要事先知道要执行此操作的密钥。

      + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
      {
          NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
      
          if ([key isEqualToString:@"allRecentCurrencyData"]) {
             NSArray *affectingKeys = @[@"recentBrazilReals", @"recentEuEuro",@"recentUkPounds",@"recentJapanYen"];
            keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
          }
          return keyPaths;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多