4

我知道块是客观的 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。

4

3 回答 3

5

根据“过渡到 ARC 发行说明”,您必须复制存储在字典中的块(强调我的):

当您在 ARC 模式下将块向上传递到堆栈时,块“正常工作”,例如在返回中。您不必再调用 Block Copy。 在将堆栈“向下”传递到 和其他执行保留的方法时,您仍然需要使用[^{} copy]arrayWithObjects:

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

于 2013-07-20T13:59:24.673 回答
0

我知道块是客观的 c 对象,可以在使用 ARC 时直接放入 NSDictionary 而无需 Block_copy。

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

于 2013-07-20T14:01:27.823 回答
0

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

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

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

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

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

于 2013-07-22T08:25:07.897 回答