Mike Ash创建了一个使用工作表blocks to handle callbacks的示例,这看起来非常好。这又被更新为用户Enchilada在beginSheet: block alternative?的另一个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和街区的初学者,都无法让它发挥作用。我应该如何修改代码以使其与ARC一起使用?
我知道Block_release()的内容需要去,但我无法通过关于将'void *'转换为'void(^)(NSInteger)'的编译错误而无法使用ARC。
答案 0 :(得分: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
强制转换为__bridge_transfer
(Objective-C中的通用对象类型)。这个演员告诉ARC它应该释放contextInfo
。由于block
变量是__strong(默认限定符),因此保留了块,并且在方法结束时,它最终被释放。最终结果是block
在方法结束时被释放,这是预期的行为。
或者,您可以使用-fno-objc-arc
编译该类别。 Xcode允许在启用或不启用ARC的情况下构建同一项目中的文件。