我有一个KVO观察到NSMutableArray
属性的视图控制器:
@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
可能包含不可变对象(NSDictionnary
s)而我没有用类似的东西替换它们的可变等价物:
[self.value replaceObjectAtIndex:0 withObject:[self.value[0] mutableCopy]];
不触发KVO通知。
答案 0 :(得分:1)
如果您重视时间,请不要在致电observeValueForKeyPath:...
期间添加或删除KVO观察结果。有关详细信息,请阅读我对this other question的回答。
如果您确实需要这样做,可以添加一个用作停止递归的标志的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,这两种习惯都适用于更多的防弹代码。
最后,你不是在这里做这件事,但看起来你正在路上,所以我会提到它:请记住,观察一个集合与观察中的所有项目是不一样的/ em>一个集合。