Pregunta

ACTUALIZACIÓN | He subido un proyecto de ejemplo utilizando el panel y estrellarse aquí: http: // w3style. co.uk/~d11wtq/BlocksCrash.tar.gz (sé que el "Elegir ..." botón no hace nada, no he implementado todavía).

ACTUALIZACIÓN 2 | Acaba de descubrir que ni siquiera tengo para invocar cualquier cosa en newFilePanel con el fin de causar un accidente, que simplemente hay que usarla en un comunicado.

Esto también provoca un accidente:

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

Parece ser lo último que vierten a la consola es a veces esto: "No se puede desmontar dyld_stub_objc_msgSend_stret.", Y en ocasiones esto:. "No se puede acceder en la dirección de memoria 0xa"

He creado mi propia hoja (una subclase NSPanel), que trata de proporcionar una API similar a NSOpenPanel / NSSavePanel, en el que se presenta como una hoja e invoca un bloque cuando haya terminado.

Esta es la unión:

//
//  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

Y los métodos clave dentro de la aplicación:

-(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];
}

Todo esto funciona siempre que mi bloque es sólo un no-op con un cuerpo vacío. Mi bloque en invoca cuando se desestimó la hoja.

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

Pero tan pronto como intento acceder al panel desde el interior del bloque, lo bloquee con EXC_BAD_ACCESS. Por ejemplo, este se bloquea:

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

No está claro desde el depurador con la causa. El primer elemento (cero 0) en la pila sólo dice "??" y no hay nada en la lista.

Los elementos próximos (1 y 2) en la pila son las llamadas a -endSheet:returnCode: y -dismissSheet: respectivamente. Mirando a través de las variables en el depurador, nada parece estar mal / fuera del ámbito.

Yo había pensado que tal vez el panel había sido puesto en libertad (ya que está autoreleased), sin embargo, incluso llamando -retain en él justo después de la creación no ayuda.

I Am implementación de este mal?

¿Fue útil?

Solución

Es un poco extraño para que retain un parámetro en un método y release en otro, cuando ese objeto no es una variable de instancia.

Yo recomendaría hacer el bit completionHandler de su materia beginSheet una variable de instancia. No es que usted sería capaz de mostrar la hoja más de una vez en un momento de todos modos, y sería más limpia de esta manera.

Además, su EXC_BAD_ACCESS es más probable que proviene de la llamada [handler retain] en su método beginSheet:. Probablemente se esté invocando este método con algo como (por razones de brevedad):

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

Si ese es el caso, debe -copy el bloque en lugar de retenerlo. El bloque, como se ha escrito anteriormente, vive en la pila. Sin embargo, si ese marco de pila se extrae de la pila de ejecución, entonces ese bloque se ha ido. puf Cualquier intento de acceso al bloque más tarde dará lugar a un accidente, porque usted está tratando de ejecutar código que ya no existe y ha sido sustituido por la basura. Como tal, debe invocar copy en el bloque para moverlo a la pila, donde se puede vivir más allá de la vida útil de la pila en la que fue creada.

Otros consejos

Trate de definir su EDNewFilePanel con el modificador __block:

__block EDNewFilePanel *newFilePanel = [EDNewFilePanel newFilePanel];

Esto debe retener el objeto cuando el bloque se llama, que puede ser después de que el objeto Panel se libera. Como un efecto secundario no relacionado, esto hará que también hacen que sea mutable dentro del alcance de bloque.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top