【问题标题】:beginSheet: block alternative with ARC?beginSheet:用 ARC 阻止替代方案?
【发布时间】:2011-12-02 18:39:41
【问题描述】:

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

我知道 Block_release() 的东西需要删除,但我无法克服关于将 'void *' 转换为 'void (^)(NSInteger)' 的编译错误,ARC 不允许这样做。

【问题讨论】:

    标签: objective-c cocoa automatic-ref-counting objective-c-blocks cocoa-sheet


    【解决方案1】:

    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 memory-management-wise。另一方面,Block_copy() 确实复制了该块,因此您需要平衡该副本与稍后发布的版本。

    在第二种方法中,

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

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


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

    【讨论】:

    • 非常彻底的答案。非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2011-08-11
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    相关资源
    最近更新 更多