Ich hoffe, diese Erklärung ist nicht zu abstrakt, um sie zu verstehen.
Angenommen, Sie erstellen eine Klasse MyViewController
, die eine Unterklasse von UIViewController
. Sie haben nicht den Quellcode von UIViewController
.
Nun beschließen Sie, eine MyViewController
KVO verwenden, um Änderungen an der center
Eigenschaft von self.view
. Sie fügen sich also ordnungsgemäß als Beobachter ein:
- (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];
}
Das Problem dabei ist, dass Sie nicht wissen, ob UIViewController
meldet sich auch als Beobachter von self.view
's center
. Wenn dies der Fall ist, dann haben Sie möglicherweise zwei Probleme:
- Sie werden möglicherweise zweimal aufgerufen, wenn sich der Mittelpunkt der Ansicht ändert.
- Wenn Sie sich selbst als Beobachter entfernen, können Sie auch die
UIViewController
der KVO-Registrierung.
Sie brauchen eine Möglichkeit, sich als Beobachter zu registrieren, die sich von UIViewController
der KVO-Registrierung. Das ist der Punkt, an dem die context
Argument kommt. Sie müssen einen Wert für context
dass Sie sich absolut sicher sind UIViewController
est no mit als context
Argument. Wenn Sie die Registrierung aufheben, verwenden Sie das gleiche context
wieder, damit Sie nur Ihre Registrierung entfernen, nicht UIViewController
Registrierung. Und in Ihrem observeValueForKeyPath:ofObject:change:context:
Methode, müssen Sie die context
um festzustellen, ob die Nachricht für Sie oder für Ihre Oberklasse bestimmt ist.
Eine Möglichkeit, um sicher zu sein, dass Sie eine context
die nichts anderes verwendet, ist die Schaffung eines static
variabel in MyViewController.m
. Verwenden Sie es, wenn Sie sich an- und abmelden, etwa so:
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];
}
Dann in Ihrem observeValueForKeyPath:...
Methode, überprüfen Sie es wie folgt:
- (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];
}
}
Jetzt ist gewährleistet, dass Sie die KVO Ihrer Oberklasse nicht beeinträchtigen, falls sie überhaupt eine hat. Und wenn jemand eine Unterklasse von MyViewController
der auch KVO verwendet, wird es Ihre KVO nicht beeinträchtigen.
Beachten Sie auch, dass Sie für jeden Schlüsselpfad, den Sie beobachten, einen anderen Kontext verwenden können. Wenn das System Sie dann über eine Änderung informiert, können Sie den Kontext prüfen, anstatt den Schlüsselpfad zu prüfen. Die Prüfung auf Zeigergleichheit ist etwas schneller als die Prüfung auf Stringgleichheit. Beispiel:
- (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];
}
}