使用ARC时的beginSheet:block替代方案?

6

Mike Ash创建了一个示例,使用块处理来自sheets的回调,这似乎非常好。然后,用户Enchilada在另一个SO问题中更新了它以与垃圾收集一起使用,该问题位于beginSheet: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

启用垃圾回收时,这与自动引用计数(ARC)不兼容。作为一个同时对ARC和blocks都是初学者的人,我无法让它正常工作。我应该如何修改代码才能使其与ARC兼容?

我知道Block_release()需要去掉,但我无法解决编译错误,因为在ARC中将'void *'转换为'void (^) (NSInteger)'被禁止。

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)

意思是:使用__bridge转换将block转换为void *。这个转换告诉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的文件进行构建。


非常详细的回答。非常感谢! - Frost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接