3

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

私はこれを間違って実装していますか?

4

2 に答える 2

12

そのオブジェクトがインスタンス変数でない場合、retainあるメソッドのパラメーターと別のメソッドのパラメーターを使用するのは少し奇妙です。release

completionHandler私はあなたのもののビットをbeginSheetインスタンス変数にすることをお勧めします。とにかく一度に複数回シートを表示できるわけではなく、この方法の方がきれいです。

また、あなたEXC_BAD_ACCESSはおそらくあなたのメソッドの[handler retain]呼び出しから来ています。beginSheet:このメソッドは、(簡潔にするために)次のようなもので呼び出している可能性があります。

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

その場合は、ブロックを保持するのではなく、ブロックする必要があります。 -copy上で入力したブロックは、スタック上に存在します。ただし、そのスタックフレームが実行スタックからポップされると、そのブロックはなくなります。 poof 後でブロックにアクセスしようとすると、存在しなくなってガベージに置き換えられたコードを実行しようとしているため、クラッシュが発生します。そのため、ブロックを呼び出しcopyてヒープに移動する必要があります。ヒープでは、ブロックが作成されたスタックフレームの存続期間を超えて存続できます。

于 2010-12-30T06:35:40.020 に答える
-1

__block修飾子を使用して EDNewFilePanel を定義してみてください。

__block EDNewFilePanel *newFilePanel = [EDNewFilePanel newFilePanel];

これは、ブロックが呼び出されたときにオブジェクトを保持する必要があります。これは、Panel オブジェクトが解放された後かもしれません。無関係な副作用として、これにより、ブロック スコープ内で変更可能になります。

于 2010-12-30T06:24:51.953 に答える