Подтвердить что ты не робот

Значение параметра контекста в наблюдении за ключевым значением

Использование контекстного параметра в следующем методе, который используется для регистрации для уведомлений о ключевых значениях. Документация просто обозначает его как произвольный набор данных.

addObserver:self forKeyPath:@"selectedIndex" options:NSKeyValueObservingOptionNew context:nil

Может кто-то пролить некоторый свет, какая цель за ним...

Спасибо

4b9b3361

Ответ 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];
    }
}