Probleme beim verwenden von Observern

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Probleme beim verwenden von Observern

    Hallo, ich habe erst kürzlich mit der iOS Programmierung begonnen und ich bin nun am verzweifeln. Ich möchte gerne einen Timer (eigene Klasse definiert, da dieser Timer noch mehrere Methoden haben soll) auf mehren Views in einer Tab Bar Application haben. Das Problem ist ,dass wenn ich mit Observern arbeite, mir die App sofort abstürtzt.


    Ich habe im Interface-Builder zwei Views erstellt und die enstsprechenden ViewController erzeugt. Das Programm funktioniert ohne Observer. Sobald ich jedoch die Observer hinzufüge, wird mir diese Fehlermeldung angezeigt

    Quellcode

    1. 2014-01-21 00:46:45.833 Spielzeit[12136:a0b] An instance 0x8c1ae00 of class Spielzeit was deallocated while key value observers were still registered with it.
    2. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger.
    3. Here's the current observation info:
    4. <NSKeyValueObservationInfo 0x8c1d2b0> (
    5. <NSKeyValueObservance 0x8c204a0: Observer: 0x89789a0, Key path: timerwert, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x8c0f710>)
    6. 2014-01-21 00:46:45.842 Spielzeit[12136:a0b] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <SecondViewController 0x89789a0> for the key path "timerwert" from <Spielzeit 0x8973470> because it is not registered as an observer.'


    Hier nun der Code wie ich die Observer in den beiden ViewController erzeuge:

    Quellcode

    1. SecondViewController
    2. -(void)viewWillAppear:(BOOL)animated
    3. {
    4. [super viewDidAppear:animated];
    5. //Hier wird der Observer angemeldet.
    6. [spielZeit addObserver:self forKeyPath:@"timerwert" options:NSKeyValueObservingOptionNew context:NULL];
    7. _spielzeitAnzeige.text=[spielZeit formatiereAusgabe];
    8. }
    9. -(void)viewWillDisappear:(BOOL)animated
    10. {
    11. [spielZeit stoppeSpielzeit];
    12. //Hier wird er wieder abgemeldet.
    13. [spielZeit removeObserver:self forKeyPath:@"timerwert" context:NULL];
    14. [super viewWillDisappear:animated];
    15. }
    Alles anzeigen


    Quellcode

    1. ThridViewController
    2. -(void)viewWillAppear:(BOOL)animated
    3. {
    4. [super viewDidAppear:animated];
    5. //Hier wird der Observer angemeldet.
    6. [spielZeit addObserver:self forKeyPath:@"timerwert" options:NSKeyValueObservingOptionNew context:NULL];
    7. _spielzeitAnzeige.text=[spielZeit formatiereAusgabe];
    8. }
    9. -(void)viewWillDisappear:(BOOL)animated
    10. {
    11. [spielZeit stoppeSpielzeit];
    12. //Hier wird er wieder abgemeldet.
    13. [spielZeit removeObserver:self forKeyPath:@"timerwert" context:NULL];
    14. [super viewWillDisappear:animated];
    15. }
    Alles anzeigen


    Ich hoffe einer von euch kann mir einen Tipp geben,wo mein Fehler ist.


    P.S Falls viele grammatische Fehler in meinem Text sind, möchte ich mich für diese entschuldigen, komme aus Luxemburg

    Würde mich auf eine positive Antwort von euch freuen

    Viele Grüße
    ringobigno
  • Wenn ich die Fehlermeldung richtig verstehe, dann ist deine SPielzeit weg. Und wenn du dann versuchst den observer davon zu löschen kracht es. Du must erst den Observer removen und dann die Spielzeit releasen denke ich.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Hallo Thallius
    Danke für deine schnelle Antwort. Ich weiss jetzt aber nicht was du meinst. Ich habe nur die beiden Methoden in denen die
    Observer erstellt und in der andere wieder abgemeldet werden. Ich habe im moment noch nirgends was mit release stehen.
    In welcher Methode sollte dann der aufruf removeObserver stehen?
  • Das ist alles soweit richtig. Aber das WAS Du observerst nämlich deine Spielzeit, die muss eben auch da sein. Und die scheint orgendwo released zu werden bevor du den Observer removest.
    Ich kennen den Code ja nicht wo du die Spielzeit erzeugst und wieder freigibst aber dan der Stelle must du suchen.

    Alleine der Unterstrich unter der SPielzeit läßt mich vermuten, dass du dieses nicht als property benutzt und deshalb wird der irgendwann einfach nicht mehr da sein. Du solltest die SPielzeit als strong property deklarieren und nur über den setter darauf zugreifen. Wenn das, was ich jetzt geschrieben habe unklar ist, dann solltest du dringend erst noch ein paar Grundlagen lernen bevor Du mit KVO und KVC etc anfängst.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • -(void) stoppeSpielzeit
    {
    [timer invalidate];
    timer=nil;
    }

    Das ist meine stoppeSpielzeit. Wobei timer ein Typ von NSTimer ist. Ich habe aber als property den timerwert erstellt.

    @property (nonatomic,assign) int timerwert;


    Ok das mit dem Unterstrich in der Methode war ein Fehler von mir.
    Habe das Label als @property (strong, nonatomic) IBOutlet UILabel *spielzeitAnzeige in den beiden ViewController definiert.
    Also anstatt _spielzeitAnzeige.text=[spielZeit formatiereAusgabe] habe ich jetzt in beiden Controllern self.spielzeitAnzeige.text=[spielZeit formatiereAusgabe]; stehen.

    Es würde euch bestimmt einfacher fallen, wenn ich hier meinen gesammten Quellcode poste? Soll ich da jeweils nur die *.m-Dateien posten oder auch die *.h-Dateien?
  • Mike schrieb:

    Schmeiß doch erst den Observer weg und stoppe dann ;)


    Aber das mache ich doch:

    Quellcode

    1. -(void)viewWillDisappear:(BOOL)animated
    2. {
    3. [spielZeit stoppeSpielzeit]; //Hier wird gestoppt
    4. [spielZeit removeObserver:self forKeyPath:@"timerwert" context:NULL]; //und dann wird hier der Observer abgemeldet.
    5. [super viewWillDisappear:animated];
    6. }


    Ach man ich dachte, ich würde endlich was verstehen von der IOS-Programmierung. Scheint leider aber nicht der Fall zu sein :( :( :( :(
  • ringobingo schrieb:

    Mike schrieb:

    Schmeiß doch erst den Observer weg und stoppe dann ;)


    Aber das mache ich doch:

    Quellcode

    1. -(void)viewWillDisappear:(BOOL)animated
    2. {
    3. [spielZeit stoppeSpielzeit]; //Hier wird gestoppt
    4. [spielZeit removeObserver:self forKeyPath:@"timerwert" context:NULL]; //und dann wird hier der Observer abgemeldet.
    5. [super viewWillDisappear:animated];
    6. }

    Nein, Du stoppst den Timer und meldest den Observer erst danach ab. Vertausche einfach mal die Zeilen drei und vier.

    Michael
  • Michael schrieb:

    ringobingo schrieb:

    Mike schrieb:

    Schmeiß doch erst den Observer weg und stoppe dann ;)


    Aber das mache ich doch:

    Quellcode

    1. -(void)viewWillDisappear:(BOOL)animated
    2. {
    3. [spielZeit stoppeSpielzeit]; //Hier wird gestoppt
    4. [spielZeit removeObserver:self forKeyPath:@"timerwert" context:NULL]; //und dann wird hier der Observer abgemeldet.
    5. [super viewWillDisappear:animated];
    6. }

    Nein, Du stoppst den Timer und meldest den Observer erst danach ab. Vertausche einfach mal die Zeilen drei und vier.

    Michael


    Ok, habe jetzt die Zeilen vertauscht jedoch ändert sich nichts daran, dass die App abstürtzt.

    Habe jetzt als Anhang das Projekt hinzugefügt.
  • Michael schrieb:

    In ViewController.m ist's immer noch falsch herum.

    Michael

    Sorry habe vergessen diese beiden Dateien zu löschen. Die werden nicht benötigt! Es sind nur SecondViewController unt ThridViewController von nöten.



    In ViewController.m ist's immer noch falsch herum. Ach, und die Variable spielZeit, was soll die sein? Global oder eine Instanzvariable?


    Also ich habe jetzt folgendes gemacht:
    ich habe die Variable spielZeit aus dem Implementation Block genommen und in den Interface-Teil der Klasse .m gemacht.

    Vorher:

    Quellcode

    1. .
    2. .
    3. .
    4. @implementation SecondViewController
    5. Spielzeit *spielZeit;
    6. .
    7. .
    8. jetzt:
    9. SecondViewController.m
    10. @interface SecondViewController ()
    11. {
    12. Spielzeit *spielZeit;
    13. }
    14. .
    15. .
    16. .
    Alles anzeigen

    Jetzt läuft das Programm. Aber jetzt verstehe ich nicht warum es jetzt geht und vorher nicht. Ich habe jetzt eine Instanzvariable im SecondViewController definiert richtig? Und was hatte ich vorher gemacht?
    Schon jetzt mal ein Danke an euch alle die mir geholfen haben. 8) 8)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von ringobingo ()

  • ringobingo schrieb:

    Michael schrieb:

    Also ich habe jetzt folgendes gemacht:
    ich habe die Variable spielZeit aus dem Implementation Block genommen und in den Interface-Teil der Klasse .m gemacht.

    Vorher:

    Quellcode

    1. .
    2. .
    3. .
    4. @implementation SecondViewController
    5. Spielzeit *spielZeit;
    6. .
    7. .
    8. jetzt:
    9. SecondViewController.m
    10. @interface SecondViewController ()
    11. {
    12. Spielzeit *spielZeit;
    13. }
    14. .
    15. .
    16. .
    Alles anzeigen

    Jetzt läuft das Programm. Aber jetzt verstehe ich nicht warum es jetzt geht und vorher nicht. Ich habe jetzt eine Instanzvariable im SecondViewController definiert richtig? Und was hatte ich vorher gemacht?

    Vorher hattest Du eine globale Variable. D.h. obwohl Du im SecondViewController und im ThirdViewController die Variable spielZeit deklariert hast, hattest Du nur eine globale Variable. Und damit begann das Drama. Wenn Du den Tab wechselst, wird nicht erst viewWillDisappear: des bisher angezeigten ViewControllers aufgerufen, sondern vorher wird bereits viewDidLoad des neu anzuzeigenden ViewControllers aufgerufen. Und da Du in viewDidLoad der globalen Variable eine neue Spielzeit Instanz zuweist, wird die alte Instanz (dank ARC) mit der noch bestehenden Observierung freigegeben. Ohne ARC hättest Du Dir übrigens den Speicher mit Spielzeit Instanzen zugemüllt.

    Michael
  • @Michael
    Vielen Dank für deine detaillierte Erklärung. Jetzt habe ich es begriffen. Danke nochmals dass du dir die Zeit dafür genommen hast.



    @Mike
    Ich habe zwei Bücher: IPhone Apps programmieren Praxis einstieg (mitp) und Apps programmieren für IPhone und IPad (Galileo Computing). Sind diese nicht geeignet für Einsteiger?
    Auch dir Mike vielen Dank für deine Hilfe
  • Das erste Buch sagt mir so nichts, das zweite kenn ich aber und habe es für gut befunden. Wie schon Michael sagte war die globale Variable das Problem. Ich such dir heute Abend nochmal was raus was mit dem Speichermanagement zu tun hat (ich hoffe ich finde es) und recht informativ sein kann. Auch wenn man heute idR ARC (Automatic Reference Counting) benutzt ist ein Blick auf das MRC (Manuall Reference Counting) für das Verständnis des Speichermanagement recht hilfreich wenn auch es schwer zu verdauen ist.
    [self setSignature:null];
    [[self postCount] increment];