通过KVC访问收集(以保护收集并符合KVO)

时间:2010-02-09 15:56:51

标签: objective-c cocoa cocoa-touch key-value-observing key-value-coding

我有一个类Test,它有一系列Foos。我希望在不直接暴露ivar的情况下提供对Foos的访问。我正在努力使这个KVC合规(也为KVO合规铺平了道路)。我有:

Test.h

@interface Test : NSObject
{
    NSMutableArray *foos;
}

@property (readonly, copy) NSMutableArray *foos;

@end

Test.m

- (id) init
{
    self = [super init];
    if (self != nil)
    {
        foos = [[NSMutableArray array] retain];
    }
    return self;
}

- (NSMutableArray*) foos
{
    return [self mutableArrayValueForKey:@"foos"];
}

- (NSUInteger)countOfFoos
{
    return [foos count];
}

- (id)objectInFoosAtIndex:(NSUInteger)index
{
    return [foos objectAtIndex:index];
}

- (NSArray *)foosAtIndexes:(NSIndexSet *)indexes
{
    return [foos objectsAtIndexes:indexes];
}

- (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index
{
    [foos insertObject:key atIndex:index];
}

- (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes
{
    [foos insertObjects:foosArray atIndexes:indexes];
}

- (void)removeObjectFromFoosAtIndex:(NSUInteger)index
{
    [foos removeObjectAtIndex:index];
}

- (void)removeFoosAtIndexes:(NSIndexSet *)indexes
{
    [foos removeObjectsAtIndexes:indexes];
}

当客户端尝试添加Foo时,这会进入无限循环:

Test *test = [[Test alloc] init];
NSMutableArray *foos = test.foos;
[foos addObject:@"adding object"]; // infinite loop here

我做错了什么?

3 个答案:

答案 0 :(得分:2)

- (NSMutableArray*) foos
{
    return [self mutableArrayValueForKey:@"foos"];
}

访问者不应使用KVC来获取被访问属性的值;这个想法是KVC通过访问器,因为访问器比KVC更接近于值。

foos的正确实现应该返回数组的复制,可变或其他方式。我就是这样做的:

- (NSArray *) foos
{
    return [[foos copy] autorelease];
}

我也会公开所有的访问者。任何想要改变数组或随机访问特定索引中的元素的东西都可以这样做。它仍然是安全的和封装的,因为它们通过你的访问器,而不是直接访问阵列。

除非您在编写代码时不知道要访问哪个密钥,否则没有任何理由自己使用KVC协议方法。例如,如果您正在编写nib加载器或Cocoa Bindings系统,那么您将使用KVC。

答案 1 :(得分:1)

问题是mutableArrayValueForKey返回的代理NSMutableArray:首先必须通过“foos”方法获取真实数组。因为那是返回代理NSMutableArray的那个,所以它进入了一个无限循环。一种解决方案是使用另一个名称:

- (NSMutableArray*) mutableFoos
{
    return [self mutableArrayValueForKey:@"foos"];
}

答案 2 :(得分:0)

我在这个问题上花了很长时间,想通过一个访问器来解决这个问题。我想在答案中澄清那些进来的人。这就是我所做的:

@property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos;

然后明显实施:

- (NSMutableArray*)getTheFoos {
    return [self mutableArrayValueForKey:@"foos"];
}

但是要小心, getFoos 似乎是一个(未记录的)KVC访问器,因为它将它发送到同一个循环中。

然后到KVO:

Test* test= [[Test alloc] init];
NSObject* obj= [[NSObject alloc] init];
NSMutableArray* arrTheData= test.foos;

[test.foos insertObject:obj atIndex:0];
[arrFoos insertObject:obj atIndex:0];

arrFoos 可以读取更新的,变异的数组(它将包含两个对象),但插入其中不会触发KVO。在我冒险的某个地方,我看到mutableArrayValueForKey:的返回不返回NSMutableArray *,而是返回它的子类,这可能是它的原因。