1

我有一个带有NSMutableArrayKVO 观察到的属性的视图控制器:

@interface MyViewController ()

@property (strong, nonatomic) NSMutableArray *values;

@end

@implementation MyViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.values = [NSMutableArray array];

    [self addObserver:self forKeyPath:@"self.values" options:0 context:nil];
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"self.values"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"self.values"])
    {
        // The code here is a bit more complex than that, but the main idea
        // is to mutate the object
        [self.values addObject:[NSDate date]]; // This line triggers a KVO notification, causing an infinite recursion 
    }
}

@end

我实现的目标是对数组中的每个产品应用折扣,并在每次将产品添加到数组时重新计算这些折扣。

我想在收到通知时修改观察到的属性,但显然上面的代码不起作用,它会进入无限递归。

一种解决方案是在开头删除观察者observeValueForKeyPath...并在结尾重新添加观察者。

虽然这看起来有点难看,并且可能会导致问题是属性在一个线程上修改而observeValueForKeyPath...在另一个线程上运行。

编辑:正如 Wain 所指出的,这里真正的问题是我NSMutableArray self.value可能包含不可变对象(NSDictionnarys ),我无法用它们的可变等价物替换它们,例如:

[self.value replaceObjectAtIndex:0 withObject:[self.value[0] mutableCopy]];

不触发 KVO 通知。

4

1 回答 1

1

如果您珍惜时间,请不要在调用observeValueForKeyPath:.... 有关详细信息,请阅读我对其他问题的回答。

如果你真的需要这样做,你可以添加一个 ivar 作为标志来停止递归。它可能看起来像这样:

@interface MyViewController ()

@property (strong, nonatomic) NSMutableArray *values;

@end

@implementation MyViewController
{
    BOOL _updatingValuesInObservation;
}

static void * const MyValueObservation = (void*)&MyValueObservation;

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.values = [NSMutableArray array];

    [self addObserver:self forKeyPath:@"values" options:0 context:MyValueObservation];
}

- (void)dealloc
{
    [self removeObserver:self forKeyPath:@"values" context:MyValueObservation];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (MyValueObservation == context && !_updatingValuesInObservation)
    {
        _updatingValuesInObservation = YES;
        [self.values addObject:[NSDate date]];
        _updatingValuesInObservation = NO; 
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

@end

还要注意 context 参数的使用和 super 的调用,这两个好习惯都是为了获得更多的防弹代码。

最后,你不是在这里这样做,但看起来你正在路上,所以我会提到它:请记住,观察一个集合与观察一个集合中的所有项目不同

于 2013-06-17T13:36:18.850 回答