2010-12-30 12 views
3

UPDATE | Ich habe ein Beispielprojekt mit dem Panel hochgeladen und stürzte hier: http://w3style.co.uk/~d11wtq/BlocksCrash.tar.gz (Ich weiß, dass die Schaltfläche "Wählen ..." nichts tut, ich habe es noch nicht implementiert).EXC_BAD_ACCESS Aufruf eines Blocks

UPDATE 2 | Gerade entdeckt, ich muss nicht einmal auf newFilePanel irgendetwas aufrufen, um einen Absturz zu verursachen, ich muss es nur in einer Aussage verwenden.

Dies führt auch zu einem Absturz:

[newFilePanel beginSheetModalForWindow:[windowController window] completionHandler:^(NSInteger result) { 
    newFilePanel; // Do nothing, just use the variable in an expression 
}]; 

es das letzte, was auf der Konsole abgeladen erscheint, ist manchmal: „Unfähig dyld_stub_objc_msgSend_stret zu zerlegen“, und manchmal: „Can not Speicher zugreifen Adresse 0xa“ .

Ich habe mein eigenes Blatt (eine NSPanel-Unterklasse) erstellt, das versucht, eine API ähnlich wie NSOpenPanel/NSSavePanel bereitzustellen, indem es sich als ein Blatt darstellt und einen Block aufruft, wenn das erledigt ist.

Hier ist die Schnittstelle:

// 
// EDNewFilePanel.h 
// MojiBaker 
// 
// Created by Chris Corbyn on 29/12/10. 
// Copyright 2010 Chris Corbyn. All rights reserved. 
// 

#import <Cocoa/Cocoa.h> 

@class EDNewFilePanel; 

@interface EDNewFilePanel : NSPanel <NSTextFieldDelegate> { 
    BOOL allowsRelativePaths; 

    NSTextField *filenameInput; 

    NSButton *relativePathSwitch; 

    NSTextField *localPathLabel; 
    NSTextField *localPathInput; 
    NSButton *chooseButton; 

    NSButton *createButton; 
    NSButton *cancelButton; 
} 

@property (nonatomic) BOOL allowsRelativePaths; 

+(EDNewFilePanel *)newFilePanel; 

-(void)beginSheetModalForWindow:(NSWindow *)aWindow completionHandler:(void (^)(NSInteger result))handler; 
-(void)setFileName:(NSString *)fileName; 
-(NSString *)fileName; 
-(void)setLocalPath:(NSString *)localPath; 
-(NSString *)localPath; 
-(BOOL)isRelative; 

@end 

Und die wichtigsten Methoden in der Umsetzung:

-(void)beginSheetModalForWindow:(NSWindow *)aWindow completionHandler:(void (^)(NSInteger result))handler { 
    [NSApp beginSheet:self 
     modalForWindow:aWindow 
     modalDelegate:self 
     didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) 
      contextInfo:(void *)[handler retain]]; 
} 

-(void)dismissSheet:(id)sender { 
    [NSApp endSheet:self returnCode:([sender tag] == 1) ? NSOKButton : NSCancelButton]; 
} 

-(void)sheetDidEnd:(NSWindow *)aSheet returnCode:(NSInteger)result contextInfo:(void *)contextInfo { 
    ((void (^)(NSUInteger result))contextInfo)(result); 
    [self orderOut:self]; 
    [(void (^)(NSUInteger result))contextInfo release]; 
} 

Das alles funktioniert bereitgestellt mein Block ist nur ein no-op mit einem leeren Körper. Mein Block wird aufgerufen, wenn das Blatt geschlossen wird.

EDNewFilePanel *newFilePanel = [EDNewFilePanel newFilePanel]; 
[newFilePanel setAllowsRelativePaths:[self hasSelectedItems]]; 
[newFilePanel setLocalPath:@"~/"]; 
[newFilePanel beginSheetModalForWindow:[windowController window] completionHandler:^(NSInteger result) { 
    NSLog(@"I got invoked!"); 
}]; 

Aber sobald ich versuche, den Block der Platte zuzugreifen, von innen, ich mit EXC_BAD_ACCESS abstürzen. Zum Beispiel stürzt dies ab:

Es ist nicht klar aus dem Debugger mit der Ursache ist. Das erste Element (Null 0) auf dem Stapel sagt nur "??" und da ist nichts aufgeführt. Die nächsten Elemente (1 und 2) im Stapel sind die Aufrufe an -endSheet:returnCode: bzw. -dismissSheet:. Beim Durchsehen der Variablen im Debugger scheint nichts/nicht im Bereich zu sein.

Ich hatte gedacht, dass das Panel vielleicht veröffentlicht worden war (da es automatisch freigegeben wurde), aber sogar -retain direkt nach dem Erstellen anzurufen, hilft nicht.

Implementiere ich das falsch?

Antwort

12

Es ist ein wenig seltsam für Sie zu retain ein Parameter in einer Methode und release es in einem anderen, wenn dieses Objekt keine Instanzvariable ist.

Ich würde empfehlen, das completionHandler Bit Ihres beginSheet Zeug eine Instanzvariable. Es ist nicht so, als könnten Sie das Blatt trotzdem mehr als einmal auf einmal anzeigen, und es wäre auf diese Weise sauberer.

Auch Ihre EXC_BAD_ACCESS stammt am ehesten von der [handler retain] Anruf in Ihrer beginSheet: Methode. Du bist wahrscheinlich diese Methode mit so etwas wie (der Kürze halber) Aufruf:

[myObject doThingWithCompletionHandler:^{ NSLog(@"done!"); }]; 

Wenn das der Fall ist, Sie muss-copy der Block, anstatt sie zu behalten. Der Block, wie oben beschrieben, lebt auf dem Stapel. Wenn jedoch dieser Stapelrahmen vom Ausführungsstapel entfernt wird, ist dieser Block nicht mehr vorhanden. poof Jeder Versuch, später auf den Block zuzugreifen, führt zu einem Absturz, weil Sie versuchen, Code auszuführen, der nicht mehr vorhanden ist und durch den Rest ersetzt wurde. Daher müssen Sie copy für den Block aufrufen, um ihn in den Heap zu verschieben, wo er über die Lebensdauer des Stack-Frames, in dem er erstellt wurde, hinausleben kann.

+0

Brilliant, danke! Das Kopieren löst alles. Danke auch für die Eingabe, wo ich beharre/freigebe. Es fühlte sich selbst für mich merkwürdig an, aber ich folgte irgendwo in der Apple-Dokumentation einem Muster, wenn ich mich richtig erinnere (sie behielten und veröffentlichten eine NSNummer). Ein flüchtiger ivar ist wahrscheinlich weniger riskant. – d11wtq

+0

Vergessen zu fügen, Ihre Antwort war sehr klar, es macht Sinn, dass Sie es kopieren müssten, um es in diesem Fall herum zu halten, da das Panel asynchron aufgerufen wird und der Stapel natürlich endet. – d11wtq

+0

Ich empfehle auch Block_copy und Block_release –

-1

Versuchen Sie EDNewFilePanel mit dem __block Modifier definieren:

__block EDNewFilePanel *newFilePanel = [EDNewFilePanel newFilePanel]; 

Dies sollte das Objekt behalten, wenn der Block aufgerufen wird, die nach dem Panel-Objekt freigegeben wird sein kann. Als eine nicht verwandte Nebenwirkung wird dies dazu führen, dass es auch innerhalb des Blockumfangs änderbar wird.

+0

Versuchte das, aber es machte keinen Unterschied, es stürzt immer noch ab. Übrigens benötigt NSOpenPanel keine Verwendung von '__block'. Danke (PS: Ich habe einen Link zu einem Beispielprojekt in der Frage hinzugefügt). – d11wtq

+2

__block macht genau das Gegenteil von dem, was Sie beschrieben haben. – bbum

Verwandte Themen