4

私は立ち往生しています!

カスタム モーダル ダイアログを作成しようとしています。ブロックを完了ハンドラーとして使用して、NSSavePanel と同様に実行したいと考えています。

必要と思われる重要なスニペットのみをコピーしました。

@implementation ModalWindowController
    - (void)makeKeyAndOrderFront:(id)sender
                   modalToWindow:(NSWindow*)window
                      sourceRect:(NSRect)rect
               completionHandler:(void (^)(NSInteger result))handler {

        _handler = [handler retain];

        session = [NSApp beginModalSessionForWindow:[self window]];
        [[NSApplication sharedApplication] runModalSession:session];

        [[self window] makeKeyAndOrderFrontCentered:self expandingFromFrame:rect];
    }
    - (IBAction)okButtonPressed:(id)sender {
        [[self window] orderOut:self];
        _handler(NSOKButton);
        [NSApp endModalSession:session];
    }

@end

これで、コードを使用してこれを呼び出すことができます。

[self.modalWindowController makeKeyAndOrderFront:self
                                   modalToWindow:[[self view] window]
                                      sourceRect:sr
                               completionHandler:^(NSInteger result) {
    NSLog(@"Inside Block");
    if ( result == NSOKButton ) {
        // do something interesting here
    }
}];
NSLog(@"Errg");

すべてうまくいきますが、メソッド makeKeyAndOrderFront:modalToWindow:sourceRect:completionHandler: が完了した後、スレッドはブロックされないため、ユーザーが「OK」または「キャンセル」を選択していなくても「Errg」が出力されます。この時点でモーダル ウィンドウが表示され、ユーザーが [OK] をクリックすると、_handler ブロックが実行されます。ただし、ブロック内のローカル変数にアクセスしようとすると、すべてが既にクリーンアップされているため、アプリがクラッシュします。

makeKeyAndOrderFront:... メソッドからメインスレッドをブロックする最良の方法は何ですか? これは、ブロックを使用して完了ハンドラーを実装するための正しいアプローチですか?

4

1 に答える 1

5

あなたのライン

_handler=[handler retain];

する必要があります

_handler=[handler copy];

これにより、完了ハンドラーが呼び出される前にローカル変数がなくなるという問題が解決するはずです。 [handler copy]ブロック内で参照されるローカル変数を処理して、ブロックを作成したメソッドからプログラムの流れが抜けた後でもローカル変数が消えないようにします。

次の事実を思い出してください。

  1. ブロック インスタンスは、ブロック内で参照されるローカル変数をキャプチャします。
  2. ただし、ブロック インスタンスはスタック上にあります。{...}保持していても、ブロックを作成した範囲からプログラムの流れが外れると消えてしまいます。
  3. したがって、ここで行っているように、後でデータを使用する場合は、copyそれだけretainでなく、それを行う必要があります。ブロックから参照されるすべてのローカル オブジェクト変数をCopy自動的にs します。retain
  4. release使い終わったら、それを行う必要があります。ブロック自体のメモリの割り当てを解除し、release参照されているローカル オブジェクト変数にメッセージを送信します。ただし、GC を使用する場合は、これを気にする必要はありません。

ブロックの詳細を理解するには、 Mike Ash の記事がとても役に立ちました

于 2010-01-26T21:13:23.657 に答える