[solved] Entkäfern nur unter Zuhilfenahme der CrashLogs

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

  • [solved] Entkäfern nur unter Zuhilfenahme der CrashLogs

    Ich glaube, ich habe gerade eine Denkblockade, die aufgelöst werden möchte.

    Ich bekomme ein EXC_BAD_EXCESS in Thread 1, wenn ich bei einer Document Based Application das letzte verfügbare Fenster schließe.

    NSZomieEnabled erklärt mir nur:
    2011-08-11 22:48:32.641 <project>[3283:903] ZOMBIES/AFOC ARE ENABLED!!! AAAAARRRRRRGH!!! BRAINS!!!
    2011-08-11 22:48:55.634 <project>[3283:903] *** -[MyDocument respondsToSelector:]: message sent to deallocated instance 0x1018861e0

    Das ist super, hilft mir in erster Instanz aber mal kein Stück weiter.

    Das Crashlog erzählt außerdem:
    Thread 0 Crashed: Dispatch queue: com.apple.main-thread
    0 libobjc.A.dylib 0x00007fff855b3fc8 objc_msgSend_vtable5 + 16
    1 com.apple.AppKit 0x00007fff8028ffcb -[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:] + 95
    2 com.apple.Foundation 0x00007fff8464433c __NSFireDelayedPerform + 404
    3 com.apple.CoreFoundation 0x00007fff8831cbe8 __CFRunLoopRun + 6488
    4 com.apple.CoreFoundation 0x00007fff8831adbf CFRunLoopRunSpecific + 575
    5 com.apple.HIToolbox 0x00007fff829c57ee RunCurrentEventLoopInMode + 333
    6 com.apple.HIToolbox 0x00007fff829c5551 ReceiveNextEventCommon + 148
    7 com.apple.HIToolbox 0x00007fff829c54ac BlockUntilNextEventMatchingListInMode + 59
    8 com.apple.AppKit 0x00007fff80052eb2 _DPSNextEvent + 708
    9 com.apple.AppKit 0x00007fff80052801 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 155
    10 com.apple.AppKit 0x00007fff8001868f -[NSApplication run] + 395
    11 com.apple.AppKit 0x00007fff800113b0 NSApplicationMain + 364
    12 com.host.application 0x0000000100001062 main + 34 (main.m:13)
    13 com.host.application 0x0000000100001034 start + 52

    Thread 1: Dispatch queue: com.apple.libdispatch-manager
    0 libSystem.B.dylib 0x00007fff890c9c0a kevent + 10
    1 libSystem.B.dylib 0x00007fff890cbadd _dispatch_mgr_invoke + 154
    2 libSystem.B.dylib 0x00007fff890cb7b4 _dispatch_queue_invoke + 185
    3 libSystem.B.dylib 0x00007fff890cb2de _dispatch_worker_thread2 + 252
    4 libSystem.B.dylib 0x00007fff890cac08 _pthread_wqthread + 353
    5 libSystem.B.dylib 0x00007fff890caaa5 start_wqthread + 13

    Thread 2:
    0 libSystem.B.dylib 0x00007fff890caa2a __workq_kernreturn + 10
    ...


    Ich stelle fest:
    - passiert nur, wenn ich das letzte Fenster schließe
    - und zwar reproduzierbar
    - egal ob per Klick oder Appel+W

    Kommen wir also nun zum Kern des Ganzen, der How To Anfrage:
    How To the Teufel bekomme ich jetzt raus, wer mir wann warum meine Instanz von NSDocument wegtritt respektive wer wie warum danach noch damit reden will?

    Das Dokument besitzt nur zwei assigned Outlets und ein eigenes View, welches brav im dealloc auf 'nil' gesetzt wird.
    Ich habe eigentlich nur zwei Objekte in Verdacht, die vom Dokument bei jedem Klick in sich das View abfragen. Die schicken aber vorher kein -respondsToSelector.

    Wie finde ich heraus was da kaputt ist?
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von Lucas de Vil ()

  • Nicht du schickst das respondsToSelector:, sondern Cocoa, genauer die Methode -_checkForTerminateAfterLastWindowClosed: in einer Kategorie von NSApplication. Die macht das, weil sie wissen möchte, ob sich deine Anwendung terminieren soll, wenn das letzte Fenster geschlossen wurde.

    Offenkundig (Kategoriename) verhält es sich so, dass Windows von der Applikation gecacht werden. (Wusste ich auch noch nicht. Hier sieht man, dass man etwas aus Fehlern lernen kann, was in keinem unmittelbarem Zusammenhang mit dem Fehler steht. Und das sogar dan, wenn andere den Fehler machen. Die Welt kann so einfach sein.) Wenn der Cache ein retain auslöst, bedeutet dies, dass die Objekte einmal mehr gehalten werden, als es "eigentlich" notwendig ist. Deshalb gibt es auch nie Probleme, weil dieses "überzählige" retain als zusätzlicher Stützpfeiler die Statik deines Gebäudes hält. Du hast also mutmaßlich, da schließe ich mich MCDan an, irgendwo ein release zu viel, was nur die ganze Zeit nicht auffällt.

    Wird dann beim letzten Fenster das ganze Gebäude beseitigt, fällt das auf, weil der zusätzliche Stützpfeiler entfernt wird. Da dies wegen des überflüssigen release planwidrig der letzte war, fällt dir jetzt die Decke auf den Kopp. Das kann eben zu Kopfschmerzen führen.

    Fummelst du am Documentcontroller herum? Erzeugst du selbst Dokumente?
    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"?

  • Wenn ich es nachbauen könnte wüsste ich ja vermutlich schon woran es liegt.
    Ich könnte also das komplette Projekt hochladen und mir sagen lassen wo das Problem ist.
    Allerdings hilft es mir persönlich nicht beim Erweitern meiner Kenntnisse bezüglich des Anwendungsdebugging.

    Ich will ja schließlich nicht jedes mal, wenn ich ein Crashlog bekomme, euch unentgeltlich nach der Ursache suchen lassen. ;)

    MCDan schrieb:

    Sieht so aus als hättest Du einem MyDocument Objekt irgendwo in Deiner App ein release zuviel geschickt.
    Lass das Projekt mal mit Build and Analyze übersetzen und schaue Dir mal alle release Aufrufe an MyDocument Objekte genau an.

    Danke für den Tipp mit der Anzeige der Release-Aufrufe!

    Zum ersten Punkt (da er sich mit folgendem deckt) später etwas.

    Amin Negm-Awad schrieb:


    Nicht du schickst das respondsToSelector:, sondern Cocoa, genauer die Methode -_checkForTerminateAfterLastWindowClosed: in einer Kategorie von NSApplication. Die macht das, weil sie wissen möchte, ob sich deine Anwendung terminieren soll, wenn das letzte Fenster geschlossen wurde.

    Offenkundig (Kategoriename) verhält es sich so, dass Windows von der Applikation gecacht werden. (Wusste ich auch noch nicht. Hier sieht man, dass man etwas aus Fehlern lernen kann, was in keinem unmittelbarem Zusammenhang mit dem Fehler steht. Und das sogar dan, wenn andere den Fehler machen. Die Welt kann so einfach sein.) Wenn der Cache ein retain auslöst, bedeutet dies, dass die Objekte einmal mehr gehalten werden, als es "eigentlich" notwendig ist. Deshalb gibt es auch nie Probleme, weil dieses "überzählige" retain als zusätzlicher Stützpfeiler die Statik deines Gebäudes hält. Du hast also mutmaßlich, da schließe ich mich MCDan an, irgendwo ein release zu viel, was nur die ganze Zeit nicht auffällt.

    Wird dann beim letzten Fenster das ganze Gebäude beseitigt, fällt das auf, weil der zusätzliche Stützpfeiler entfernt wird. Da dies wegen des überflüssigen release planwidrig der letzte war, fällt dir jetzt die Decke auf den Kopp. Das kann eben zu Kopfschmerzen führen.

    Fummelst du am Documentcontroller herum? Erzeugst du selbst Dokumente?

    Die Methode _checkForTerminateAfterLastWindowClosed: fiel mir bereits ins Auge, vor Allem, weil ich für diese Methode überhaupt nichts kann.

    Ich setze keinen eigenen Dokumentencontroller ein. MyDocument.m tut was immer ein MyDoument.m tut.

    Lediglich zwei Views fragen eine Instanzvariable der MyDocument.m via [[NSApplication sharedApplication] delegate] ab.
    Das funktionierte allerdings erst, nachdem ich MyDocument.m auch als Delegate verküpft hatte... +grübel+

    Danke für den Denkanstoß! Ich werfe die Verknüpfung wieder raus, sobald ich zu Hause bin.
    Wenn es daran liegt muss ich irgendwie anders an die Instanz von MyDocument.m kommen, damit ich an sein hinterlegtes View komme.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Handelt es sich um eine Document-Based Application?

    An die Documents einer Document-Based Application kommt man über den NSDocumentController, welchen Du über +sharedDocumentController bekommst.

    Über currentDocument bekommst Du z.B. das gerade aktive Document der Application, wenn denn eines geöffnet ist oder halt nil, wenn die Application gerade keine Documents geöffnet hat.

    Kannst Du uns vielleicht ein paar Infos geben, warum Du von zwei Views aus auf MyDocument zugreifen möchtest?
  • [[NSDocumentController sharedDocumentController] currentDocument]...
    Klingt ausgezeichnet. :)

    Beide Views beinhalten viele Subviews.
    Ein View ist die darstellende Anzeige, es beherbergt ein Array von Subviews.
    Das zweite View ist eine Art Werkzeugleiste, es beherbergt ein Set der verfügbaren Subviews.

    Die Idee ist: ich klicke in der Werkzeugleiste ein Subview an. Wenn ich dann in die Anzeige klicke, wird das Subview an der Klickstelle durch eine Kopie des gewählten Subview in der Werkzeugleiste ausgetauscht.
    Ergo muss ich mir $irgendwo merken, welches Subview da in der Werkzeugleiste ausgewählt wurde.

    Den ersten Gedanken einer iVar in der Werkzeugleiste habe ich verworfen. Die Dokumentation zu der Nutzung von View Controllern unter Mac OS X ist ausgesprochen dürftig, so dass ich diese erst einmal nicht extra verwenden wollte.
    Also habe ich das Datum 'aktuelles Subview' in einer Instanzvariable im Application Delegate hinterlegt, um dann bequem per [[[NSApplication sharedApplication] delegate] valueForKey:@"selectedSubView"] an dieses zwischengespeicherte Subview zu kommen.

    Anders formuliert lautet der Masterplan:

    C-Quellcode

    1. // First view: Tools
    2. -(void)onClick:(NSEvent*)event
    3. {
    4. id item = [self hitTest:clickedLocation];
    5. [[[NSDocumentController sharedDocumentController] currentDocument] setCurrentSelectionItem:item];
    6. }
    7. // Second view: Representation
    8. -(void)onClick:(NSEvent*)event
    9. {
    10. NSUInteger indexOfOldObject = [[self array] indexOfObject:[self hitTest:clickedLocation]];
    11. [[self array] replaceObjectAtIndex:indexOfOldObject withObject:[[[NSDocumentController sharedDocumentController] currentDocument] currentSelectionItem]];
    12. }
    Alles anzeigen


    Ich gehe davon aus, dass die Anwendung an sich optimierungsbedürftig sein wird, sofern sie dann mal fertig ist. (ETA ~8 Stunden)
    Ich wollte sie halt nur erst einmal fertig haben und dann herumoptimieren.

    Nur sind Crashbehebungen halt keine Opmtimierungen. ;)
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P
  • Joah, die Verbindung 'delegate' von Application zu File's Owner rausgenommen und es kachelt nicht mehr ab.

    Im Prinzip recht logisch.
    Das Fenster wird geschlossen und die Instanz von MyDocument wird freigegeben.
    Anschließend fragt die Anwendung ihr Delegate MyDocument, ob sie sich jetzt beenden soll.
    Da die letzte Instanz (welch Wortwitz!) von MyDocument aber fehlt, kommt diese Anfrage nie an.
    Das Aufspüren dieses Fehlers ist auch ein bissl schwieriger, da er in den Tiefen von Cocoa und dem XIB auftritt.

    Immerhin drei Dinge gelernt:
    1) myDocument ist kein AppDelegate, und das ist auch gut so™ aus oben genanntem Grund
    2) herausgefunden wie die Zombies aktiviert werden
    3) CrashLog und NSZombieEnabled sind eine hilfreiche Kombination, wenn man ihr Gestammel erst einmal versteht.
    «Applejack» "Don't you use your fancy mathematics to muddle the issue!"

    Iä-86! Iä-64! Awavauatsh fthagn!

    kmr schrieb:

    Ach, Du bist auch so ein leichtgläubiger Zeitgenosse, der alles glaubt, was irgendwelche Typen vor sich hin brabbeln. :-P