removeObserver:forKeyPath: und observationInfo

  • removeObserver:forKeyPath: und observationInfo

    Hallo,

    nochmals zu Bindings... so langsam klappt es immer besser. Danke nochmal für die großartige Hilfe.

    Mein custom view, an dem ich arbeite und Bindings/KVO beibringe sieht so aus: christian-kienle.de/ebh.png - verbessert hoffentlich die Vorstellung...

    Immer dann falls der Benutzer aus der linken Liste einen Karteikasten auswählt entferne ich das View als Observer (bei den Karteikarten, die vor dem Wechsel des Karteikastens angezeigt wurden) und füge das View als Observer wieder hinzu (bei den neu zu anzeigenden Karteikarten).

    Entfernt wird mit: removeObserver:forKeyPath:

    Diese Methode beschwert sich, falls der angegeben Observer den keypath nicht observiert. Also dann, falls etwas entfernt werden soll, was nicht existiert. Ich muss also eine Möglichkeit finden, ob mein view den keypath schon oberviert oder nicht. Bisher mache ich das mit:

    - (void *)observationInfo

    Das checke ich gegen nil. Diese Methode kann ja allerdings von den observierten Objekten überschrieben werden und die können ja auch einfach konstant "NULL/nil" liefern. Dann crasht meine App - im hinblick auf zukunftsfähigkeit würde ich dies gerne anders lösen.

    Gibts da was?
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • RE: removeObserver:forKeyPath: und observationInfo

    Neee, eigentlich solltest du nicht nachfragen, ob eine Observirung vorliegt, sondern dies "automtisch" richtig machen, also strukturell.

    Ich verstehe es auch nicht ganz. Dein View ist doch irgendwie an die Selection gebunden.Änderungen müssten mitgteilt werden, nicht wahr? Dann kannst du doch die alte Observierung wegwerfen und die neue anmelden. Dann werden nur welche weggeworfen, die du bereits angeeldet hast. (Beim binden selbst musst du natürlich das serste Mal anmelden.)

    Hinweis noch: Die to-Many-KeyPath sind etwas schräg. Nehemen wir mal:

    selection.cards.title

    selection wird auf das Startobject (Controller) angewendet. Der liefert die Selektion und dann wird auf das gelieferte Objekt cards angewendet. Das liefert dann ein Array von Cards. Dann wird hierauf wieer title angewendet … halt Stopp, das stimmt nicht! Ein Array hat kein Attrbut title. Vielmehr muss das auf die einzelnen Elemente angewendet werden. Dies bedeutet, dass hier ein "Trippelschritt" notwendig ist.

    Deshalb ist das im IB auch getrennt. Und wenn du mal UI-Elemente von Hand bndest, wirst du auch bemerken, dass da was nicht stimmt.

    Ich löse das wie folgt:
    Eine Bindung für das Array, also bei dir etwa. selection.cards.
    Die einzelnen Eigenschaften erhalten entweder eine eigene Bindung, von der ich dann einfach den ersten Teil wegwerfe
    selection.cards.titile -> title
    und dann nur noch den hinteren Teil anwende. Es kann ja ohnehin nicht ande sein! Inzwischen mache ich daraus aber gar kein Binding mehr, sondern habe eine Methode setTitleKeyPath, die als Parameter @"title" bekommt.

    Das ist deutlichkonsistenter und einfacher.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Sorry - muss nochmal "stören".

    Sobald der User einen anderen Karteikasten auswählt wird:

    Quellcode

    1. - (void)setKarteikasten:(id)neuerKarteikasten;


    aufgerufen. Dies erreiche ich durch ein entsprechendes Binding meines Views an die Selection des ArrayControllers, der die Karteikästen kontrolliert.

    Nun muss ich genau in diesem Zeitpunkt mein View bei den in dem Karteikasten enthaltenen Karteikarten als Observer registrieren.

    Das habe ich so gemacht: (ist eigentlich nicht so wichtig - aber für diejenigen, die immer Code sehen wollen sicherlich befriedigend).

    Quellcode

    1. - (void)_addAsObserver {
    2. NSEnumerator *cardsEnumerator = [[[self content] arrangedObjects] objectEnumerator];
    3. EBHCard *card = nil;
    4. while(card = [cardsEnumerator nextObject]) {
    5. NSEnumerator *cardsViewColumnsEnumerator = [[self cardsViewColumns] objectEnumerator];
    6. EBHCardsViewColumn *cardsViewColumn = nil;
    7. while(cardsViewColumn = [cardsViewColumnsEnumerator nextObject]) {
    8. [card addObserver:self forKeyPath:[cardsViewColumn sideKeyPath] options:nil context:nil];
    9. }
    10. }
    11. }
    Alles anzeigen


    Aber - ich will ja auch das View bei einem Wechsel der Selection erstmal als Observer entfernen. Dazu habe ich auch eine Methode - _removeAsObserver.

    Der "Algorithmus" sieht so aus:

    * User wählt neuen Karteikasten
    * View als Observer zunächst "entfernen" und dann
    * das View als Observer bei den Karteikarten im neu ausgewähltem Karteikasten eintragen.

    Ist doch richtig so - oder?

    Falls ja: Beim ersten Aufruf von:

    Quellcode

    1. - (void)setKarteikasten:(id)neuerKarteikasten;


    ist mein View ja noch kein Observer. Daher mein Versuch das mit - (void *)observationInfo abzufangen.
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • Ich kann es mir jetzt nicht genau anschauen, aber: Bereits beim -bind:… musst duu zum ersten Mal die Observer einrichten. Dann startest du mit eingerichteten Observern und kann st bei jeder Änderung diese wegnehmen und neue einrichten.

    Beim -unbind nimmst du dann weg.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Also - ich versuche mein "Setup" nochmals zu beschreiben:

    Ich habe zwei NSArrayController in meiner nib-file:

    1) NSArrayController *karteikästen;
    2) NSArrayController *karteikarten;

    1) hat einen ManangedObjectContext und enthält alle Karteikästen.
    2) ist an die selection.cards von 1) gebunden.

    Ich habe zusätzlich noch mein eigenes NSView (im folgenden mit 3) bezeichnet).

    3) exposed ein "Karteikasten" und ein "Content" Binding. In awakeFromNib meines NSApp Delegates habe ich das View gebunden:

    Quellcode

    1. [view bind:@"karteikasten" toObject:karteikästen withKeyPath:@"selection.karteikastenObjekt" options:nil]


    Du schreibst in deinem OP:

    "Bereits beim -bind:… musst duu zum ersten Mal die Observer einrichten."

    Gut - die Idee hört sich fantastisch an, denn ich rufe ja in awakeFromNib [view bind:...] auf. Im Graphics Binding Example wird es übrigens auch so gemacht, wie du es beschreibst. Allerdings klappt das bei mir nicht.

    Zu dem Zeitpunkt, in dem "bind:toObject:withKeyPath:options:" des Views aufgerufen wird ist bei meinem Karteikasten Array Controller nocht keine Selektion vorhanden - also sind auch keine Karteikarten zum observieren da. Daher macht es ja wenig Sinn in "bind:toObject:withKeyPath:options:" des Views irgendwelche Logik zu implementieren.

    Meine Lösungsansätze: Vor dem Binden des Views per Code eine Selection des Karteikästenarraycontrollers definieren oder das view in einer Methode, die mit einem Delay aufgerufen wird binden.

    Sind meine Beobachtungen/Schlussfolgerungen richtig?
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].
  • Meine Freundin wartet, daher ganz kurz:

    Es ist gleichgültig, ob die Selektion vorhanden ist. Du bindest ja das ARRAY-Binding an die Selektion. Sobald eine Selektion da ist, bekommst ud das dann mit und kannst deine objekt-kvo-einrichten.

    Klar?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Original von Tom9811
    Meine Freundin wartet, daher ganz kurz:

    Es ist gleichgültig, ob die Selektion vorhanden ist. Du bindest ja das ARRAY-Binding an die Selektion. Sobald eine Selektion da ist, bekommst ud das dann mit und kannst deine objekt-kvo-einrichten.

    Klar?


    Danke. Ich glaube jetzt hats "klick" gemacht. Hatte die ganze Zeit das View nicht auch beim Controller als Observer angemeldet.
    Die Objective-Cloud ist fertig wenn sie fertig ist. Beta heißt Beta.

    Objective-C und Cocoa Band 2: Fortgeschrittene
    Cocoa/Objective-C Seminare von [co coa:ding].