【问题标题】:Objective-C KVO doesn't work with C unionsObjective-C KVO 不适用于 C 联合
【发布时间】:2013-01-12 16:47:14
【问题描述】:

我需要在使用 KVO 的 Objective-C 类上观察联合类型的属性,但我似乎没有运气。我做了一些实验:只要我使用 C 结构,一切正常。一旦我用联合替换结构,自动 KVO 就不再起作用(observeValueForKeyPath 不会被调用)。

这是我的小测试课:

AppDelegate.h:

#import <Cocoa/Cocoa.h>

typedef union {
    float data[3];
    struct {
        float x,y,z;
    };
} vec3union;

typedef struct {
    float x,y,z;
} vec3struct;

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;

@property (assign) vec3struct vectorStructValue;

@property (assign) vec3union vectorUnionValue;

@end

AppDelegate.m:

@implementation AppDelegate

@synthesize vectorStructValue = _vectorStructValue;
@synthesize vectorUnionValue = _vectorUnionValue;

- (void)dealloc
{
    [super dealloc];
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [self addObserver:self forKeyPath:@"vectorStructValue" options:NSKeyValueObservingOptionNew context:nil];
    [self addObserver:self forKeyPath:@"vectorUnionValue" options:NSKeyValueObservingOptionNew context:nil];

    self.vectorStructValue = (vec3struct){1,2,3};
    self.vectorUnionValue = (vec3union){4,5,6};
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    NSLog(@"keyPath %@ did change, object: %@", keyPath, [object description]);
}

@end

输出:

2013-01-12 17:38:26.447 KVOTest[57522:303] keyPath vectorStructValue did change, object: <AppDelegate: 0x100614200>

是我做错了什么,还是 Objective-C 运行时/KVO 实现中的错误或缺失功能?

注意:我知道我可以通过覆盖属性设置器手动实现这一点,但这不是这个问题的重点。答案应该让我知道为什么自动 KVO 在这种情况下不起作用。

更新:为了清楚起见,这是一个简单的测试用例,将结构属性上的 KVO 观察者与联合属性上的观察者进行比较。这些属性不相关。他们有独立的 ivars 和独立的内存后备存储。您可以删除 struct 属性并运行测试,仍然是相同的结果 - union 属性没有 KVO 观察者事件。

【问题讨论】:

  • 或者更好的是,明确说明您对值的更改(willChangeValueForKey:didChangeValueForKey:);
  • 内存布局不应该对 assignment/KVO 机制起作用。即使联合的值不正确,也应该调用观察者。当覆盖valueForKey: 并从联合字节返回手动生成的NSValue 时,通过willChangeValueForKey:didChangeValueForKey: 手动发出更改有效。但是,这不是问题的主题。
  • 它不适用于“observeValueForKeyPath:”,但可以使用“didChangeValueForKey:”。奇怪。
  • 在我的实际应用中,当没有覆盖 valueForKey: 并返回手动构造的 NSValue 对象时,didChangeValueForKey: 出现异常。
  • 哇,真正的 SSCCE!我还没有在狗的年龄见过周围的人。我看到了同样的结果;这是一个有趣的发现。

标签: objective-c key-value-observing unions


【解决方案1】:

OP 的问题中没有相关属性。

我误读了发烧引起的幻觉。

工会在 KVO/KVC 中完全被淘汰了。留下下面的文字,因为它仍然很有趣。


KVO 不能通过观察记忆或玩任何此类棘手的恶作剧来工作。它通过动态创建子类、覆盖 setter 方法以及在调用 setter 时自动调用 willChange.../didChange... 方法来工作。

因此,您实际上拥有 2 个属性1 个支持 存储。然而,就 KVO 而言,它们彼此完全隔离。

你想要的是依赖键。您可以使用+keyPathsForValuesAffectingValueForKey: 在两个键之间创建依赖关系,这样调用任一 setter 都会触发另一个属性的更改。

不知道是否支持相互依赖;如果它支持有效的循环依赖。

或者,您应该能够覆盖设置器以调用 willChange/didChange 以获取其他属性(以及正在更改的属性)。


如果您希望willChange/didChange任一属性更改时为两个键触发,则将使用相关键。 IE。如果您对结构感到不满,则联合实际上会发生变化,并且联合属性的观察者应该看到意志/确实变化以响应设置结构版本。

我刚刚测试过。你是对的。工会有些奇怪。它完全坏了。以上所有内容仍然正确,但没有任何好处。

雷达归档:rdar://problem/13003794


哦……好样的。带工会的 KVO 根本不起作用。运行时似乎根本无法识别该类有一个名为 vectorUnionValue 的键。

我补充说:

+ (NSSet *)keyPathsForValuesAffectingVectorStructValue
{
    return [NSSet setWithObject:@"vectorUnionValue"];
}

+ (NSSet *)keyPathsForValuesAffectingVectorUnionValue
{
    return [NSSet setWithObject:@"vectorStructValue"];
}

导致运行时异常的原因:

2013-01-12 12:05:11.877 djkdfjkdfjkdf[51598:303] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AppDelegate 0x10010a520> valueForUndefinedKey:]: this class is not key value coding-compliant for the key vectorUnionValue.'

【讨论】:

  • 我不认为这是正确的。为什么应该只有一个后备商店?实际上,您可以删除 struct 属性,这只是为了在我的示例中进行说明。它不会改变自动 KVO 实现对 union 属性不起作用这一事实。至于 willChange/didChange 实现:我知道我可以手动完成,问题是为什么它不能自动工作(因为它应该)?
  • 感谢您的编辑,但我仍然不明白这应该如何与依赖键、多个属性和后备存储的数量相关。另外,我没有指出 KVO 会做一些神奇的记忆观察。如果你能澄清这一点,那就太好了,所以我可以考虑接受你的回答。谢谢!
  • 抱歉,我不会接受这个答案,即使还有 10 个人要支持它。属性以某种方式相互关联的想法是完全错误的。也许我应该更改问题中的示例,使其不再包含 struct 属性。只是为了解决这个问题,问题实际上只是关于联合财产上明显失效的 KVO。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
  • 2011-08-07
相关资源
最近更新 更多