更新 | パネルを使用してクラッシュするサンプル プロジェクトをここにアップロードしました: 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
作成した直後に呼び出しても役に立ちません。
私はこれを間違って実装していますか?