gpt4 book ai didi

iphone - 如何调试 KVO

转载 作者:太空狗 更新时间:2023-10-30 03:59:03 26 4
gpt4 key购买 nike

在我的程序中,我手动使用 KVO 来观察对象属性值的变化。我在自定义 setter 中的以下代码行收到 EXC_BAD_ACCESS 信号:

[self willChangeValueForKey:@"mykey"];

奇怪的是,当工厂方法调用自定义 setter 并且周围不应该有任何观察者时,就会发生这种情况。我不知道如何调试这种情况。

更新:列出所有注册观察者的方法是observationInfo。事实证明,确实列出了一个指向无效地址的对象。但是,我完全不知道它是如何到达那里的。

更新 2:显然,同一个对象和方法回调可以为给定对象注册多次 - 导致观察对象的 observationInfo 中有相同的条目。删除注册时,只会删除其中一个条目。这种行为有点违反直觉(在我的程序中添加多个条目肯定是一个错误),但这并不能解释虚假的观察者如何神秘地出现在新分配的对象中(除非有一些缓存/重用正在进行我不知道的事情)。

修改后的问题:我怎样才能知道一个对象在何时何地被注册为观察者?

更新 3:具体示例代码。

ContentObj 是一个具有字典作为名为 mykey 的属性的类。它覆盖:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"mykey"]) {
automatic = NO;
} else {
automatic=[super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}

一些属性具有 getter 和 setter,如下所示:

- (CGFloat)value {
return [[[self mykey] objectForKey:@"value"] floatValue];
}
- (void)setValue:(CGFloat)aValue {
[self willChangeValueForKey:@"mykey"];
[[self mykey] setObject:[NSNumber numberWithFloat:aValue]
forKey:@"value"];
[self didChangeValueForKey:@"mykey"];
}

容器类具有 NSMutableArray 类的属性 contents,它包含 ContentObj 类的实例。它有几种手动处理注册的方法:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"contents"]) {
automatic = NO;
} else {
automatic=[super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}

- (void)observeContent:(ContentObj *)cObj {
[cObj addObserver:self
forKeyPath:@"mykey"
options:0
context:NULL];
}

- (void)removeObserveContent:(ContentObj *)cObj {
[cObj removeObserver:self
forKeyPath:@"mykey"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (([keyPath isEqualToString:@"mykey"]) &&
([object isKindOfClass:[ContentObj class]])) {
[self willChangeValueForKey:@"contents"];
[self didChangeValueForKey:@"contents"];
}
}

容器类中有几个修改内容的方法。它们看起来如下:

- (void)addContent:(ContentObj *)cObj {
[self willChangeValueForKey:@"contents"];
[self observeDatum:cObj];
[[self contents] addObject:cObj];
[self didChangeValueForKey:@"contents"];
}

还有一些为数组提供类似功能的其他对象。他们都通过添加/删除自己作为观察者来工作。显然,任何导致多次注册的行为都是错误,并且可能隐藏在这些方法中的某处

我的问题针对如何调试这种情况的策略。或者,请随时提供实现这种通知/观察者模式的替代策略。

更新 4:我发现这个 bug 是使用断点、NSLog、代码审查和出汗的组合。我没有在 KVO 中使用上下文,尽管这绝对是另一个有用的建议。这确实是一个错误的双重注册——出于我无法理解的原因——导致了观察到的行为。

实现包括[self willChange...]; [self didChange...] 如描述的那样工作(在 iOS 5 上),尽管它远非美丽。问题在于,由于 NSArray 不符合 KVO,因此无法讨论其内容的更改。我还考虑过 Mike Ash 所建议的通知,但我决定使用 KVO,因为这似乎是一种更像 Cocoa 的机制来完成这项工作。这可以说不是最好的决定......

最佳答案

是的,调用 -addObserver: 两次将导致两次注册。类 Foo 和 Foo 的某个子类 Bar 可能都(合法地)注册相同的通知,但具有不同的上下文(始终包含上下文,始终检查 -observeValueForKeyPath 中的上下文并始终调用 super在 -observeValueForKeyPath 中)。

这意味着 Bar 的实例将注册两次,这是正确的。

但是,您几乎肯定不希望多次注册相同的对象/键路径/上下文,正如@wbyoung 所说,重写 -addObserver:forKeyPath:options:context: 应该有所帮助你确保这不会发生。如果有必要,请跟踪数组中的观察者/键路径/上下文并确保它们是唯一的。

Mike Ash has some interesting thoughts and code on his blog about using contexts .他说它被破坏是对的,但实际上 KVO 是完全可用的。

也就是说,当您使用它来做某事时,它就意味着要做。以前是绝对做不到这样的..

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

因为这是谎言。当您调用 -willChange.. 时,'contents' 的值必须与您调用 -didChange.. 时的值不同。 KVO 机制将在 -willChangeValueForKey-didChangeValueForKey 中调用 -valueForKey:@"contents" 来验证值是否已更改。这显然不适用于数组,因为无论您如何修改内容,您仍然拥有相同的对象。现在我不知道情况是否仍然如此(网络搜索一无所获)但请注意 -willChangeValueForKey, -didChangeValueForKey are not the correct way to handle manual kvo of a collection.为此,Apple 提供了替代方法:-

– willChange:valuesAtIndexes:forKey:
– didChange:valuesAtIndexes:forKey:
– willChangeValueForKey:withSetMutation:usingObjects:
– didChangeValueForKey:withSetMutation:usingObjects:

值必须更改可能仍然不是真的,但如果是这样,您的方案将行不通。

我会做的是收到一份关于修改您的收藏的通知。以及该集合中项目修改的不同通知。也就是说,此时你正试图触发@“contents”的通知,而你可能有@“contents”和@“propertiesOfContents”。您需要观察两个键路径,但您可以使用自动 kvo 而不是手动触发通知。 (使用自动 kvo 将确保调用正确版本的 -willChange.. -didChange..)

对于数组的自动 kvo,请查看(不需要 NSArrayController):- Key-Value-Observing a to-many relationship in Cocoa

然后每次将一个项目添加到集合中时,观察您需要的属性(就像您现在所做的那样),并在它们更改时翻转 self.propertiesOfContents 的值。 (好吧,当我读回它时,它听起来并不一定比你的解决方案简单,但我仍然相信它可能表现得更好)。

关于iphone - 如何调试 KVO,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8093188/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com