nsview --> [self nextResponder] --> exc_bad_access

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

  • nsview --> [self nextResponder] --> exc_bad_access

    Hallo,

    eine OSX App zeigt per [NSApp beginSheet:...] ein Sheet mit Einstellungen. Der zugehörige ViewController verwendet hierfür eine Eine NSTableView die eine eigene Unterklasse von NSView als Inhalt für die Zeilen verwendet:

    Quellcode

    1. - (NSView *)tableView:(NSTableView *)tb viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    2. ...
    3. MyTableRowView *rowView = [[MyTableRowView alloc] init];
    4. rowView.delegate = self.RowHandler;
    5. ...
    6. return rowView;
    7. }


    Quellcode

    1. // MyTableRowView.h
    2. ...
    3. @property (nonatomic, unsafe_unretained) NSObject <MyTableRowViewDelegate> *delegate;
    4. // MyTableRowView.m
    5. ...
    6. - (void)mouseEntered:(NSEvent *)theEvent {
    7. if ([self.delegate respondsToSelector:@selector(mouseEnteredRow:)])
    8. [self.delegate mouseEnteredRow:self];
    9. [[self nextResponder] mouseEntered:theEvent];
    10. }
    Alles anzeigen



    Problem 1:
    Wir das Sheet geschlossen wird dieses animiert ausgeblendet. Scheinbar werden schon während dieser Animation der ViewController und damit auch der "RowHandler" (der Delegate der MyTableRowViews) freigegeben). Befindet sich die Maus in dem Bereich über dem VC reagieren die MyTableRowViews dennoch weiter auf mouseEntered: Wird dort auf den bereits freigegebenen Delegate zugegriffen kommt es zum EXC_BAD_ACCESS und die App crashed entsprechend.

    Die neue Version der App läuft nur noch auf 10.7+. Dieses Problem konnte ich beheben in dem ich den Delegate statt als unsafe_unretained als weak definiert habe:

    Quellcode

    1. @property (nonatomic, weak) NSObject <MyTableRowViewDelegate> *delegate;


    Während unsafe_unretained ungültigen Speicher zurück lässt, setzt weak das Ganze auf NIL, der Zugriff auf das freigegebene Objekt ist also unproblematisch. Ist das die richtige Lösung oder macht man das anders?


    Problem 2:
    Durch die Änderung des Delegate crashed die App nicht mehr beim Zugriff auf diesen. Dafür kommt es jetzt zum EXC_BAD_ACCESS beim Zugriff auf [self nextResponder]. Wie kann ich das vermeiden/lösen?


    Vielen Dank!
  • Aber ist es dann nicht besser dem entsprechendem RowView anders mitzuteilen, dass er jetzt seinen HOVER Effekt zeigen soll? Sprich, ist es nicht besser, in der NSTableView Subklasse einfach nur der row unter dem Cursor zu sagen: changeHoverStyle oder so? Dann behält die Tabelle ihre vollen Funktionen und Du musst nicht berücksichtigen, ob es überhaupt einen NextResponder gibt.

    Volker
  • Ich verwende keine Subklasse von NSTableView sondern NSTableView direkt. Natürlich gäbe andere Möglichkeiten der Implementierung, aber NSTabelView abzuleiten damit diese den jeweiligen Row-Views sagt, dass diese ihren Zustand ändern müssen halte ich für keine gute Lösung. Das wäre unverhältnismäßig viel Aufwand. Zudem besteht eigentlich kein Grund dazu, dass die TableView die RowViews kennt und weiß wie diese zu verwenden sind. Die RowView will die Darstellung ändern wenn die Maus darüber ist? Gut, dann soll die RowView sich auch darum kümmern...
  • volker schrieb:

    Dann prüfe doch ob es einen nextResponder gibt


    Tja, da wäre nur die Frage wie man das macht könnte... NSView gibt den NextReponder ja nur über [self nextResponder] zurück. Sobald vom auf [self nextResponder] zugreift kommt es zum Crash. Was genau schlägst du also vor? Soweit ich weiß gibt es keine Möglichkeit zu prüfen ob ein Object deallocated wurde. Aber ich lasse mich gerne eine Besseren belehren. Darum frage ich ja :)
  • Du benötigst die responder-Geschichte für mouse-hovering nicht. Wie machst Du das denn, mit einer NSTrackingArea? Hier mal Beispielcode:

    Quellcode

    1. @interface MMHoverTableCellView ()
    2. @property (strong) NSTrackingArea *trackingArea;
    3. @end
    4. @implementation MMHoverTableCellView
    5. - (void)mouseEntered:(NSEvent *)theEvent {
    6. self.mouseInside = YES;
    7. }
    8. - (void)mouseExited:(NSEvent *)theEvent {
    9. self.mouseInside = NO;
    10. }
    11. - (void)updateTrackingAreas
    12. {
    13. [super updateTrackingAreas];
    14. [self ensureTrackingArea];
    15. if (![[self trackingAreas] containsObject:self.trackingArea]) {
    16. [self addTrackingArea:self.trackingArea];
    17. }
    18. }
    19. - (void)ensureTrackingArea {
    20. if ( self.trackingArea == nil) {
    21. self.trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil];
    22. }
    23. }
    Alles anzeigen

    Entspricht mehr oder weniger dem Beispielprojekt von Apple. In mouseEntered musst Du m.W. nicht super bzw. den nextResponder aufrufen, die Doku verlangt das nicht und bei mir klappt es auch einwandfrei. Im Viewcontroller kannst Du dann mouseInside observieren oder eben das ganze mit delegation lösen.

    Zu Deiner Frage mit der weak-property bei delegates: ja, das macht man genau so. (Eventuell beim Zugriff auf das Delegate die property einer strong-Referenz zuweisen:

    Quellcode

    1. @property (weak) id<DelegateProtokoll> delegate;
    2. ...
    3. // delegate nutzen
    4. id<DelegateProtokoll> strongDelegate = self.delegate;
    5. // ab jetzt strongDelegate nutzen

    Das verhindert, dass Dir das delegate unter dem Hintern weggezogen wird.

    Gruß, Markus