KVO集合

时间:2013-06-03 12:34:43

标签: ios cocoa-touch nsmutablearray key-value-observing kvc

我在我的收藏中添加了一个观察者,并观察它的计数

[[[JHTaskSave defaults] tasks] addObserver:self forKeyPath:@"count" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

当我将一个对象添加到我的集合中时,JHTaskSave是一个单例对象,并且任务是符合JHTaskCollection KVC的:

[[[JHTaskSave defaults] tasks] addTask:newTask]

任务计数发生变化但是没有调用observeValueForKeyPath,我不明白为什么

这是我的收藏类:

@interface JHTaskCollection : NSObject <NSFastEnumeration>
{
    NSMutableArray      *_tasks;
}

@property (nonatomic) NSUInteger count;

- (id)taskAtIndex:(NSUInteger)index;
- (void)addTask:(JHTask *)task;
- (void)removeTask:(JHTask *)task;
- (void)insertObject:(id)key inTasksAtIndex:(NSUInteger)index;
- (void)removeObjectFromTasksAtIndex:(NSUInteger)index;
- (void)removeTaskAtIndexes:(NSIndexSet *)indexes;
- (NSArray *)taskAtIndexes:(NSIndexSet *)indexes;

@end


@implementation JHTaskCollection

- (id)init
{
    if(self = [super init]) {
        _tasks = [[NSMutableArray alloc] init];
    }
    return self;
}

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len
{
    return [_tasks countByEnumeratingWithState:state objects:stackbuf count:len];
}

- (NSArray *)taskAtIndexes:(NSIndexSet *)indexes
{
    return [_tasks objectsAtIndexes:indexes];
}

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

- (void)removeObjectFromTasksAtIndex:(NSUInteger)index
{
    [_tasks removeObjectAtIndex:index];
}

- (void)removeTaskAtIndexes:(NSIndexSet *)indexes
{
    [_tasks removeObjectsAtIndexes:indexes];
}

- (JHTask *)taskAtIndex:(NSUInteger)index
{
    return [_tasks objectAtIndex:index];
}

- (NSUInteger)count
{
    return _tasks.count;
}

- (void)addTask:(JHTask *)task
{
    [_tasks addObject:task];
}

- (void)removeTask:(JHTask *)task
{
    [_tasks removeObject:task];
}

@end

3 个答案:

答案 0 :(得分:1)

根据您发布的代码,如果您希望count成为Key-Value Observable,则需要在以某种方式改变集合时发送willChangeValueForKeydidChangeValueForKey通知这改变了计数。使用您的代码:

@implementation JHTaskCollection

- (id)init
{
    if(self = [super init]) {
        _tasks = [[NSMutableArray alloc] init];
    }
    return self;
}

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len
{
    return [_tasks countByEnumeratingWithState:state objects:stackbuf count:len];
}

- (NSArray *)taskAtIndexes:(NSIndexSet *)indexes
{
    return [_tasks objectsAtIndexes:indexes];
}

- (void)insertObject:(id)key inTasksAtIndex:(NSUInteger)index
{
    [self willChangeValueForKey: @"count"];
    [_tasks insertObject:key atIndex:index];
    [self didChangeValueForKey: @"count"];
}

- (void)removeObjectFromTasksAtIndex:(NSUInteger)index
{
    [self willChangeValueForKey: @"count"];
    [_tasks removeObjectAtIndex:index];
    [self didChangeValueForKey: @"count"];
}

- (void)removeTaskAtIndexes:(NSIndexSet *)indexes
{
    [self willChangeValueForKey: @"count"];
    [_tasks removeObjectsAtIndexes:indexes];
    [self didChangeValueForKey: @"count"];
}

- (JHTask *)taskAtIndex:(NSUInteger)index
{
    return [_tasks objectAtIndex:index];
}

- (NSUInteger)count
{
    return _tasks.count;
}

- (void)addTask:(JHTask *)task
{
    [self willChangeValueForKey: @"count"];
    [_tasks addObject:task];
    [self didChangeValueForKey: @"count"];
}

- (void)removeTask:(JHTask *)task
{
    [self willChangeValueForKey: @"count"];
    [_tasks removeObject:task];
    [self didChangeValueForKey: @"count"];
}

@end

答案 1 :(得分:0)

有两个原因导致count的通知未触发,这两者都与键值编码有关。

第一个原因是因为调用countaddTask:属性未更新。 count属性与_tasks数组的计数之间没有直接链接。操作数组的任何代码都必须包含在-willChangeValueForKey:-didChangeValueForKey:调用之间。但是,KVO协议提供了设置从属密钥的能力。由于count属性受数组影响,因此可以使用

设置这两个键之间的依赖关系
+ (NSSet*) keyPathsForValuesAffectingCount {
  return [NSSet setWithObject:@"tasks"];
}

或更通用的+ keyPathsForValuesAffectingValueForKey:(NSString *)key。实施任一项都会在count被修改时触发tasks的通知。

这不起作用的第二个原因是因为您没有按照Key-Value Coding Programming Guide中定义的符合键值的方式更新tasks数组。基本上,有序集合中的对象必须添加

-insertObject:in<Key>AtIndex:
-insert<Key>:atIndexes:

或者使用will|didChangeValueForKey:来电。

在您的示例中,如果在设置密钥依赖项后提供以下实现,则应观察更改。

- (void)addTask:(JHTask *)task
{
  [self insertObject:task inTasksAtIndex:[_tasks count]];
}

答案 2 :(得分:-2)

-(NSInteger) count是一种方法,因此,您无法使用KVO来监控它 - 您可以使用AOP,但我认为这不是您想要的方向。

也许相反,你可以直接监控集合,而不是它的数量,虽然我不确定你是否可以用KVO做这个(我认为你不能) - 你需要添加一个addObject:方法并在那里做你的魔法?

编辑:

您可以使用this blog中列出的'方法调配',但值得添加的默默无闻吗?