Ответ 1
Я надеюсь, что это объяснение не слишком абстрактно, чтобы понять.
Предположим, вы создали класс MyViewController
, который является подклассом UIViewController
. У вас нет исходного кода UIViewController
.
Теперь вы решили сделать MyViewController
использование KVO для наблюдения за изменениями в свойстве center
self.view
. Поэтому вы должным образом добавляете себя в качестве наблюдателя:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:NULL];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center"];
[super viewDidDisappear:animated];
}
Проблема заключается в том, что вы не знаете, если UIViewController
также регистрируется как наблюдатель self.view
center
. Если это так, то у вас могут быть две проблемы:
- Вы можете вызвать дважды, когда изменится центр просмотра.
- Когда вы удаляете себя как наблюдателя, вы также можете удалить регистрацию
UIViewController
KVO.
Вам нужен способ зарегистрироваться как наблюдатель, который отличается от UIViewController
регистрации KVO. То, где приходит аргумент context
. Вам нужно передать значение для context
, что вы абсолютно уверены, что UIViewController
не используется в качестве аргумента context
. Когда вы отмените регистрацию, вы снова используете тот же context
, чтобы удалить регистрацию, а не UIViewController
. И в вашем методе observeValueForKeyPath:ofObject:change:context:
вам нужно проверить context
, чтобы узнать, подходит ли сообщение для вас или для вашего суперкласса.
Один из способов убедиться, что вы используете context
, который больше ничего не использует, - это создать переменную static
в MyViewController.m
. Используйте его при регистрации и отмене регистрации, например:
static int kCenterContext;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:&kCenterContext];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center" context:&kCenterContext];
[super viewDidDisappear:animated];
}
Затем в вашем методе observeValueForKeyPath:...
проверьте его следующим образом:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
// This message is for me. Handle it.
[self viewCenterDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
Теперь вам гарантировано не мешать вашему суперклассу KVO, если он это делает. И если кто-то создает подкласс MyViewController
, который также использует KVO, он не будет мешать вашему KVO.
Обратите внимание, что вы можете использовать другой контекст для каждого ключевого пути, который вы наблюдаете. Затем, когда система уведомляет вас об изменении, вы можете проверить контекст вместо проверки пути ключа. Тестирование для равенства указателя выполняется немного быстрее, чем проверка равенства строк. Пример:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
[self viewCenterDidChange];
// Do not pass it on to super!
} else if (context == &kBackgroundColorContext) {
[self viewBackgroundDidChange];
// Do not pass it on to super!
} else if (context == &kAlphaContext) {
[self viewAlphaDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}