6

Mike Ash 创建了一个使用块来处理来自工作表的回调的示例,这看起来非常好。这反过来又被更新以使用用户 Enchilada 在beginSheet 的另一个 SO 问题中进行垃圾收集:块替代?, 见下文。

@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 和 blocks 的初学者,无法让它发挥作用。我应该如何修改代码以使其与 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)

意思如下:强制block转换为void *使用强制转换__bridge。这个演员表告诉 ARC 它不应该管理操作数的所有权,所以 ARC 不会触及block内存管理。另一方面,Block_copy()确实复制了该块,因此您需要在该副本与稍后的发布之间取得平衡。

在第二种方法中,

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

含义如下:使用强制转换强制contextInfo转换为id(Objective-C 中的通用对象类型)__bridge_transfer。这个演员告诉 ARC 它应该释放contextInfo. 由于block变量是 __strong (默认限定符),因此 Block 被保留,并在方法结束时最终被释放。最终结果是block在方法结束时被释放,这是预期的行为。


或者,您可以使用-fno-objc-arc. Xcode 允许在启用或不启用 ARC 的情况下构建同一项目中的文件。

于 2011-12-04T05:56:49.390 回答