В моей программе я использую KVO вручную, чтобы наблюдать изменения в значениях свойств объекта.Я получаю сигнал EXC_BAD_ACCESS
в следующей строке кода внутри пользовательского установщика:
[self willChangeValueForKey:@"mykey"];
Странно то, что это происходит, когда фабричный метод вызывает пользовательский установщик, и вокруг не должно быть никаких наблюдателей.,Я не знаю, как отладить эту ситуацию.
Обновление: Путь к списку всех зарегистрированных наблюдателей - observationInfo
.Оказалось, что в списке действительно есть объект, указывающий на неверный адрес.Тем не менее, я вообще не знаю как он туда попал.
Обновление 2: По-видимому, один и тот же объект и обратный вызов метода могут быть зарегистрированы несколько раз для данногообъект - в результате идентичные записи в наблюдаемом объекте observationInfo
.При удалении регистрации удаляется только одна из этих записей.Это поведение немного нелогично (и, конечно, это ошибка в моей программе вообще добавлять несколько записей), но это не объясняет, как ложные наблюдатели могут таинственным образом отображаться в недавно выделенных объектах (если не происходит некоторое кеширование / повторное использование).о чем я не знаю).
Измененный вопрос: Как я могу выяснить, ГДЕ и КОГДА объект был зарегистрирован в качестве наблюдателя?
Обновление3: Специальный пример кода.
ContentObj
- это класс со словарем в качестве свойства с именем mykey
.Он переопределяет:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"mykey"]) {
automatic = NO;
} else {
automatic=[super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
Пара свойств имеет методы получения и установки следующим образом:
- (CGFloat)value {
return [[[self mykey] objectForKey:@"value"] floatValue];
}
- (void)setValue:(CGFloat)aValue {
[self willChangeValueForKey:@"mykey"];
[[self mykey] setObject:[NSNumber numberWithFloat:aValue]
forKey:@"value"];
[self didChangeValueForKey:@"mykey"];
}
Класс контейнера имеет свойство contents
класса NSMutableArray
, которое содержит экземплярыкласс ContentObj
.У него есть несколько методов, которые обрабатывают регистрации вручную:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"contents"]) {
automatic = NO;
} else {
automatic=[super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
- (void)observeContent:(ContentObj *)cObj {
[cObj addObserver:self
forKeyPath:@"mykey"
options:0
context:NULL];
}
- (void)removeObserveContent:(ContentObj *)cObj {
[cObj removeObserver:self
forKeyPath:@"mykey"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (([keyPath isEqualToString:@"mykey"]) &&
([object isKindOfClass:[ContentObj class]])) {
[self willChangeValueForKey:@"contents"];
[self didChangeValueForKey:@"contents"];
}
}
В классе контейнера есть несколько методов, которые изменяют contents
.Они выглядят следующим образом:
- (void)addContent:(ContentObj *)cObj {
[self willChangeValueForKey:@"contents"];
[self observeDatum:cObj];
[[self contents] addObject:cObj];
[self didChangeValueForKey:@"contents"];
}
и несколько других, которые предоставляют функциональность, аналогичную массиву.Все они работают, добавляя / удаляя себя в качестве наблюдателей.Очевидно, что все, что приводит к множественным регистрациям, является ошибкой и может скрываться в этих методах где-то .
Мой вопрос нацелен на стратегии по отладке подобного рода ситуаций.В качестве альтернативы, пожалуйста, не стесняйтесь предоставить альтернативную стратегию для реализации этого типа шаблона уведомления / наблюдателя.
Обновление 4: Я нашел ошибку, используя смесь точек останова, NSLog
s,Кодовые обзоры и потоотделение.Я не использовал контекст в KVO, хотя это определенно еще одно полезное предложение.Это была действительно неправильная двойная регистрация, которая - по причинам, не зависящим от меня - привела к наблюдаемому поведению.
Реализация, включающая [self willChange...]; [self didChange...]
, работает так, как описано (на iOS 5), хотя она далеко не прекрасна.Проблема в том, что, поскольку NSArray
не является KVO-совместимым, нет возможности говорить об изменениях в его содержимом.Я также подумал об уведомлениях, предложенных Майком Эшем, но я решил пойти с KVO, так как это выглядело как более Cocoa
-ишевой механизм для выполнения работы.Возможно, это было не лучшее решение ...