6

我需要在使用 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 观察者事件。

4

1 回答 1

10

这些属性在 OP 的问题中不相关。

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

工会在 KVO/KVC 中被彻底摧毁了。留下下面的文字,因为它仍然很有趣。


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

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

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

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

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


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

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

雷达归档: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.'
于 2013-01-12T18:38:09.040 回答