2009-08-16 2 views
1

Nach Apple und zahlreichen Beispielen, die ich gesehen habe, gibt es kein Problem mit KVO/KVC, um selbst zu beobachten. Auch nach den gleichen Quellen, es ist kein Problem, das Aufstellen von mit addObserver: forKeyPath: Optionen: Kontext: in einem Init-Methode des Objekts, a la:Odd Problem mit addObserver: forKeypath: Optionen: Kontext: in Init Methode

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
    [self addObserver:self 
       forKeyPath:@"selected" 
        options:NSKeyValueObservingOptionNew 
        context:NULL]; 
    } 
    return self; 
} 

Leider aus irgendeinem Grunde, mein Beobachter Methode nicht werde gerufen wenn ich es dort mache. Wenn ich den addObserver Anruf an eine andere Methode zu bewegen und dann diese Methode in der aufrufenden Methode aufrufen:

MyObject *newObj = [[MyObject alloc] init]; 
[newObj setupObservers]; 

Dann ist alles in Ordnung. Dies ist eine Unterklasse von NSImageView, also ist es nicht so, dass es hier eine 'watchFromNib'-Alternative gibt ... Ich kratze mir hier wirklich den Kopf und bin mir sicher, dass ich etwas Offensichtliches vermisse - wie eine Regel über Dinge, die das tun Ursache KVO auf Selbst nicht in Init-Methoden arbeiten, aber ich habe nichts in den Dokumenten gefunden, die mir hier Hinweise geben würde.

Was weiß ich nicht?

+1

Dies ist keine Antwort auf Ihre Frage, aber Sie sollten immer einen eindeutigen Zeiger als Wert für den Kontext übergeben, nicht NULL. Wenn NSImageView oder eine Unterklasse Ihrer Klasse beginnt, die ausgewählte Eigenschaft von self zu beobachten, würde alles schief gehen, wenn Sie beide auf diese Weise NULL verwenden würden. –

Antwort

0

Ich bin nicht sicher, ob es eine solche Beschränkung ist oder nicht, aber selbst wenn Sie Sie kann man keine awakeFromNib haben erstellen, indem setupObservers auf die Laufschleife in Ihrer init-Methode hinzu:

[[NSRunLoop currentRunLoop] 
    performSelector:@selector(setupObservers) 
    target:self 
    argument:nil 
    order:1 
    modes:NSDefaultRunLoopMode]; 
+1

Sicher, ich habe wenig Zweifel, dass das funktionieren würde, aber scheint das nicht wie ein Hack? Außerdem sagt mir das jetzt nicht wirklich, warum es überhaupt nicht funktionieren würde - was bedeutet, dass es immer noch Informationen gibt, die mir nicht bewusst sind, was möglicherweise schlechte Programmiergewohnheiten meinerseits sind. Ich würde lieber die Dinge so machen, wie sie sollen, weil ich es richtig mache ... Weißt du? – Chronor

+0

performSelector: afterDelay und seine Varianten sind mächtige Methoden, die leicht missbraucht werden können. Dies ist ein Missbrauch. Was passiert, wenn ausgewählte Änderungen zwischen jetzt und wann der Selektor ausgelöst wird? Was ist, wenn wir etwas tun, das uns dazu bringt, nicht mehr zu beobachten? Woops haben noch nicht angefangen. Es ist am besten, diese Methode zu verwenden, um Probleme zu "reparieren". Es gibt große Verwendungen dafür, das gehört einfach nicht dazu. –

+0

@Jon, @Chronor: Das ist definitiv ein Hack. Es könnte nur ein funktionierender Hack sein, und manchmal ist das gut genug ... Wenn Sie sich "SetupObserver" selbst nennen können, dann ist es natürlich besser. –

0
Diese

ist eine Unterklasse von NSImageView, so es ist nicht wie es irgendeine ‚awakeFromNib'-Typ alternative hier ...

ich nicht underst und dieser Punkt. Erstellen Sie dieses Objekt in einer NIB oder nicht? Wenn eine NIB dieses Objekt erstellt, wird es -awakeFromNib aufrufen. Das erste, was Sie einrichten sollten (mit NSLog()) ist, ob Ihre -init tatsächlich ausgeführt wird. Wenn nichts passiert, bedeutet das normalerweise, dass der Code nicht ausgeführt wurde.

+0

Vielleicht hätte ich klarer sein sollen. Ja, es ist eine Unterklasse von NSImageView, aber es ist eine, die ich als Teil einer anderen Aktion in Code instanziiere. Ich füge es dann als Unteransicht zu einer vorhandenen benutzerdefinierten In-Window-Ansicht hinzu. Daher keine watchFromNib. Entschuldigung für die Verwirrung. – Chronor

4

Das Problem ist wahrscheinlich, dass -init in Ihrem Fall nicht aufgerufen wird, -initWithCoder: ist.

Jede Cocoa-Klasse hat eine Reihe von Init-Methoden, die als "designed initializers" bezeichnet werden. Jedes Objekt, so wie es instanziiert wird, geht garantiert durch einen und nur einen der bezeichneten Initialisierer jeder Klasse in seinem Vererbungsbaum.

Wenn Sie eine Klasse unterklassifizieren und eine Initialisierung durchführen müssen, müssen Sie alle designierten Initialisierer der Oberklasse überschreiben.

Die entworfenen Initialisierer von NSImageView sind -initWithCoder: und initWithFrame :. Überschreiben Sie diese beiden, nicht init.

+0

Auch "ausgewählt" ist zu allgemein für eine Methode, die zu einer Unterklasse eines NSMageView hinzugefügt werden kann. Was ist, wenn Cocoa entschied, NSControl oder NSImageView eine -selected-Methode hinzuzufügen? Die Semantik würde wahrscheinlich nicht zu deiner passen, und es würde Schlechtigkeit folgen. Sehen Sie, wenn Sie eine spezifischere Beschreibung Ihrer Verwendung als "ausgewählt" finden können, und wenn nicht, verwenden Sie ein Präfix. – user96459

+0

Jemand findet mich einen Idiot Stick, mit dem ich mich schlagen kann. Wie kann ich die Tatsache übersehen, dass init nicht der Initialisierer für diese Klasse ist? Natürlich war es das und die entsprechende Änderung behob das Problem. Danke, dass du mich erinnert hast! Und ja, ich stimme zu, dass "ausgewählt" ist wahrscheinlich nicht der beste Name der Eigenschaft zu verwenden. Ich werde mir etwas anderes einfallen lassen. Danke ... – Chronor

2

Was den Kontextzeiger, der bevorzugte Weg ist:

static void *MyPrivateObservationContext = (void*)@"MyPrivateObservationContext"; // we assume MyPrivateObservationContext is a unique name, I use something of the form ClassNamePropertyObservationContext

dann

-[obj add....... context:&MyPrivateObservationContext]; 

Dann in

-(void)observeValueForKeyPath:....context:c; 
{ 
    if (c == &MyPrivateObservationContext) { 
     // do work 
    } else { 
     [super observeValueForKeyPath:...]; 
    } 
} 
0

Grundsätzlich Sie versuchen, eine KVO-Benachrichtigungen hinzufügen zu einem Objekt, das noch nicht initialisiert wurde (Ihre init Funktion fügt das hinzu Beobachter, bevor du das Selbst zurückbringst).Bewegen Sie den folgenden Code ein:

[self addObserver:self 
     forKeyPath:@"selected" 
      options:NSKeyValueObservingOptionNew 
      context:NULL]; 

in die - (void)viewDidLoad statt. Alles wird gut.

+0

Ich dachte das. Aber ist es nicht wahr, dass, wenn self = [super init] an der Spitze der init aufgerufen wird, self bereit ist, als Beobachter an irgendeinem Punkt später in der überschreibenden init Methode hinzugefügt zu werden? – eddy

+0

Ja, aber der Punkt ist stattdessen der keyPath. Der keyPath muss vor dem Hinzufügen des Beobachters initialisiert werden. In Ihrem Fall scheint ein BOOL zu denken, also würde die Initialisierungstheorie nicht funktionieren. Versuchen Sie dies zu deklarieren als _ in den Header ausgewählt und in die Eigenschaft mit der @ synthesize als ausgewählt = _selected ausgewählt. Das sollte den Job machen. – DennyLou

Verwandte Themen