更新 | 我已经上传了一个使用面板并崩溃的示例项目: http://w3style.co.uk/~d11wtq/BlocksCrash.tar.gz (我知道“选择...”按钮什么也不做,我还没有实现它)。
更新2 | 刚刚发现我甚至不需要在newFilePanel
上调用任何东西就能导致崩溃,我只需要在语句中使用它。
这也会导致崩溃:
[newFilePanel beginSheetModalForWindow:[windowController window] completionHandler:^(NSInteger result) {
newFilePanel; // Do nothing, just use the variable in an expression
}];
看起来控制台打印的最后一条信息有时是这样的:"无法反汇编dyld_stub_objc_msgSend_stret。",有时是这样的:"无法访问地址0xa的内存。"
我创建了自己的面板(NSPanel子类),试图提供类似于NSOpenPanel / NSSavePanel的API,通过呈现自己作为面板并在完成时调用块。
这是接口:
//
// 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
实现中的关键方法:
-(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];
}
只要我的块是一个没有任何操作和空主体的无操作,则所有这些都可以工作。当表格被关闭时,我的块被调用。
EDNewFilePanel *newFilePanel = [EDNewFilePanel newFilePanel];
[newFilePanel setAllowsRelativePaths:[self hasSelectedItems]];
[newFilePanel setLocalPath:@"~/"];
[newFilePanel beginSheetModalForWindow:[windowController window] completionHandler:^(NSInteger result) {
NSLog(@"I got invoked!");
}];
但是当我试图从块内部访问面板时,就会崩溃并出现EXC_BAD_ACCESS错误。例如,下面的代码就会导致崩溃:
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);
}];
从调试器中不清楚原因。栈中的第一项(零0)只是显示“??”,没有列出任何内容。
栈中的下一项(1和2)是对-endSheet:returnCode:
和 -dismissSheet:
的调用。在调试器中查看变量,没有异常/超出范围的情况。
我曾经认为可能是面板已被释放掉(因为它是自动释放的),但即使在创建后立即调用-retain
也无济于事。
我是否实现有误?