【问题标题】:NSDictionary of Blocks as argument cause overflowNSDictionary of Blocks 作为参数导致溢出
【发布时间】:2013-07-20 13:53:15
【问题描述】:

我知道块是客观的 c 对象,在使用 ARC 时可以直接放入 NSDictionary 而无需 Block_copy。 但我收到此代码的 EXC_BAD_ACCESS 错误:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self method1:^(BOOL result){
        NSLog(@"method1WithBlock finished %d", result);
    }];
}

- (void) method1:(void (^)(BOOL))finish{

    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:^(NSData *rcb){
        finish(YES);
    }, @"success",
                          ^(NSError *error){
                              finish(NO);
                         }, @"failure",  nil];

    [self method2:dict];
}


- (void) method2:(NSDictionary *)dict{
    void (^success)(NSData *rcb) = [dict objectForKey:@"success"];
    success(nil);
}

如果我将 method1: 更改为此,不会引发错误。

- (void) method1:(void (^)(BOOL))finish{
    void (^success)(NSData *)  = ^(NSData *rcb){
        finish(YES);
    };

    void (^failure)(NSError *error) = ^(NSError *error){
        finish(NO);
    };
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:success, @"success",
                          failure, @"failure",  nil];
    [self method2:dict];
}

谁能解释为什么我必须在将块放入字典之前使用自动变量来存储它们?

我正在使用 iOS SDK 6.1。

【问题讨论】:

    标签: objective-c objective-c-blocks


    【解决方案1】:

    根据"Transitioning to ARC Release Notes",你必须复制一个存储的块 在字典中(强调我的):

    当您在 ARC 模式下将块向上传递到堆栈时,块“正常工作”,例如 作为回报。您不必再调用 Block Copy。 在将堆栈“向下”传递到 arrayWithObjects: 和其他做保留的方法

    第二种方法“偶然”起作用,因为 successfailure__NSGlobalBlock__ 而不是需要复制到堆中的“基于堆栈的块”。

    【讨论】:

    • 那么这两个method1:的区别是栈上的块字面量和堆上的块对象?
    • __NSGlobalBlock__ 记录在案了吗?
    • @user1122742:实际上,当您在第一个method1: 的调试器中键入“po dict”时,您会看到success__NSMallocBlock__error 是@987654334 @。差异可能是由于dictionaryWithObjectsAndKeys: 采用变量参数列表这一事实引起的。我只是将其保留在实现细节中,并坚持文档清楚地说明您必须复制放入数组或字典中的块。
    • @user1122742:我认为__NSGlobalBlock__ 没有记录,它是一个实现细节。您可以查看cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html 以获得一些指导性解释(但该文章是 ARC 之前的文章)。如果你真的很勇敢,请阅读clang.llvm.org/docs/Block-ABI-Apple.html :-)
    • @abbood:是的,这就是我理解我链接到的文档的方式。
    【解决方案2】:

    我理解块是客观的c对象,使用ARC时可以直接放入NSDictionary而不用Block_copy。

    不,它们不是普通对象。当你创建一个块时,它在堆栈上,它的保留计数是多少并不重要,当你退出函数时,它将从堆栈中弹出。复制它以使其保持活力。

    【讨论】:

      【解决方案3】:

      在以下情况下,您必须在将块传递给方法之前复制块:1) 块的存储时间超过调用的持续时间;2) 传递给它的参数是普通对象指针类型(即 @987654321 @ 或 NSObject *) 而不是块类型。

      您的电话就是这种情况。 dictionaryWithObjectsAndKeys: 将参数存储在结果字典中,它只需要普通的对象指针参数(id 类型),并且不知道您是否正在传递块。

      我所说的第二个条件的原因是因为如果方法参数已经采用块类型(例如对于任何完成处理程序参数),那么该方法已经知道块的特殊内存管理要求,因此将采用如果需要存储块,则负责复制块。在这种情况下,调用者不需要担心它。但是,在您的情况下,您将它传递给一个不知道它正在获取块的通用方法,因此不知道复制它。所以调用者必须这样做。

      谁能解释为什么我必须使用自动变量来存储 将它们放入字典之前的块?

      关于这一点,您的第二个示例恰好可以工作,因为最新版本的 ARC 编译器对块非常保守,并且每当您将其分配给块类型变量时都会插入副本。但是,ARC 规范不保证这一点,也不保证将来可以在其他编译器中工作。你不应该依赖这种行为。

      【讨论】:

        猜你喜欢
        • 2021-10-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多