6

Mike Ash は、ブロックを使用してシートからのコールバックを処理する例を作成しました。これは非常に良さそうです。これは、beginSheet の別の SO の質問で、ユーザー Enchilada によるガベージ コレクションで動作するように更新されました: block alternative? 、 下記参照。

@implementation NSApplication (SheetAdditions)

- (void)beginSheet:(NSWindow *)sheet modalForWindow:(NSWindow *)docWindow didEndBlock:(void (^)(NSInteger returnCode))block
{  
  [self beginSheet:sheet
    modalForWindow:docWindow
     modalDelegate:self
    didEndSelector:@selector(my_blockSheetDidEnd:returnCode:contextInfo:)
       contextInfo:Block_copy(block)];
}

- (void)my_blockSheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
  void (^block)(NSInteger returnCode) = contextInfo;
  block(returnCode);
  Block_release(block);
}

@end

GC を有効にすると、これは自動参照カウント (ARC) では機能しません。ARCとブロックの両方の初心者である私自身は、それを機能させることができません。ARC で動作するようにするには、コードをどのように変更すればよいですか?

Block_release() を削除する必要があることはわかりましたが、「void *」から「void (^)(NSInteger)」へのキャストが ARC で許可されていないというコンパイル エラーを回避できません。

4

1 に答える 1

14

ARC はvoid *、Block_* 関数が引数として期待する への変換を好みません。ARC は保持不可能な型の所有権について推論できないためです。関連するオブジェクトの所有権を管理する方法、または所有権をまったく管理しないように ARC に伝えるには、ブリッジ キャストを使用する必要があります。

以下のコードを使用して、ARC の問題を解決できます。

- (void)beginSheet:(NSWindow *)sheet
    modalForWindow:(NSWindow *)docWindow
       didEndBlock:(void (^)(NSInteger returnCode))block
{  
    [self beginSheet:sheet
       modalForWindow:docWindow
        modalDelegate:self
       didEndSelector:@selector(my_blockSheetDidEnd:returnCode:contextInfo:)
          contextInfo:Block_copy((__bridge void *)block)];
}


- (void)my_blockSheetDidEnd:(NSWindow *)sheet
                 returnCode:(NSInteger)returnCode
                contextInfo:(void *)contextInfo
{
    void (^block)(NSInteger) = (__bridge_transfer id)contextInfo;
    block(returnCode);
}

最初の方法では、

Block_copy((__bridge void *)block)

次のことを意味します: キャストからキャストblockvoid *使用する__bridge。このキャストは、オペランドの所有権を管理すべきではないことを ARC に伝えるため、ARC はblockメモリ管理に関して触れません。一方、Block_copy()ブロックをコピーするので、後でそのコピーとリリースのバランスを取る必要があります。

2番目の方法では、

void (^block)(NSInteger) = (__bridge_transfer id)contextInfo;

次のことを意味します: キャストcontextInfoを使用してid(Objective-C の汎用オブジェクト型) に__bridge_transferキャストします。このキャストは、ARC にリリースする必要があることを伝えcontextInfoます。変数は __strong (デフォルトの修飾子) であるためblock、ブロックは保持され、メソッドの最後で最終的に解放されます。最終結果はblock、メソッドの最後に解放されることです。これは予想される動作です。


または、そのカテゴリを でコンパイルすることもできます-fno-objc-arc。Xcode では、同じプロジェクト内のファイルを、ARC が有効になっているかどうかに関係なくビルドできます。

于 2011-12-04T05:56:49.390 に答える