Mam nadzieję, że to wyjaśnienie nie jest zbyt abstrakcyjne, aby zrozumieć.
Załóżmy, że tworzymy klasę MyViewController
, która jest podklasą UIViewController
. Nie masz kodu źródłowego UIViewController
.
Teraz postanawiasz, aby MyViewController
używać KVO do obserwowania zmian w właściwości center
z self.view
. Więc należycie dodać siebie jako obserwator:
- (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];
}
Problem polega na tym, że nie wiem, czy UIViewController
rejestruje się również jako obserwator self.view
„s center
. Jeśli tak, możesz mieć dwa problemy:
- Możesz zostać wywołany dwukrotnie, gdy centrum widoku się zmieni.
- Gdy usuniesz siebie jako obserwatora, możesz również usunąć rejestrację KVO z
UIViewController
.
Potrzebujesz sposobu, aby zarejestrować się jako obserwator, który jest odróżnialny od rejestracji KVO na UIViewController
. W tym miejscu pojawia się argument context
. Musisz podać wartość dla context
, że masz absolutną pewność, że UIViewController
jest , a nie, używając jako argumentu context
. Po wyrejestrowaniu ponownie użyjesz tego samego numeru context
, aby usunąć rejestrację, a nie UIViewController
. A w twojej metodzie observeValueForKeyPath:ofObject:change:context:
musisz sprawdzić, czy wiadomość jest dla ciebie, czy dla twojej superklasy.
Jednym ze sposobów, aby upewnić się, że używasz context
, którego nic nie używasz, jest utworzenie zmiennej static
w MyViewController.m
. Użyj go podczas rejestracji i wyrejestrowania, tak:
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];
}
Następnie w metodzie observeValueForKeyPath:...
, sprawdź to tak:
- (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];
}
}
teraz jesteś nie gwarantowane do kolidować z nadklasy za KVO, jeśli to robi każdy. A jeśli ktoś stworzy podklasę o numerze MyViewController
, która również używa KVO, nie będzie kolidować z twoim KVO.
Pamiętaj też, że możesz użyć innego kontekstu dla każdej ścieżki klucza, którą obserwujesz. Następnie, gdy system powiadomi Cię o zmianie, możesz sprawdzić kontekst zamiast sprawdzać ścieżkę klucza. Testowanie wskaźnika równości jest trochę szybsze niż sprawdzanie równości łańcuchów.Przykład:
- (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];
}
}
możliwe duplikat [parametry z observeValueForKeyPath: ofObject: Zmiana: Kontekst:] (http://stackoverflow.com/questions/1625575/parameters-from-observevalueforkeypathofobjectchangecontext) –