在块崩溃中分配NSError的调用方法

Ale*_*lex 3 cocoa objective-c objective-c-blocks automatic-ref-counting

我想了解为什么这会因EXC_BAD_ACCESS错误而崩溃.它从方法调用返回正常,但之后立即崩溃[self runMethodThatAssignsError:&error].

我在这里找到了类似的帖子,但它没有解释发生了什么,而且相当陈旧.

- (void)checkError {
    NSError *error;
    [self runMethodThatAssignsError:&error]; // crashes after returning
    NSLog(@"success");
}

- (BOOL)runMethodThatAssignsError:(NSError **)error {
    [@[@1] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        *error = [NSError errorWithDomain:@"1" code:7 userInfo:@{}];
    }];
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

Cha*_*tka 8

在Instruments中运行示例代码,似乎-[NSArray enumerateObjectsUsingBlock:]将其块包装在自动释放池中.由于NSError **指针是,默认情况下,隐含地假定__autoreleasing,你NSError当它被分配给对象会被自动释放*error,因此通过收获-[NSArray enumerateObjectsUsingBlock:]的自动释放池.

有两种方法可以解决这个问题.第一个是在块之外使用局部变量,以使ARC保留它直到枚举完成后:

- (BOOL)runMethodThatAssignsError:(NSError **)error {
    __block NSError *_error = nil;

    [@[@1] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        _error = [NSError errorWithDomain:@"1" code:7 userInfo:@{}];
    }];

    if (error) *error = _error;

    return NO;
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以将error参数声明为__strong,这将阻止首先将NSError其放入自动释放池中.请注意,只有在此方法的客户端始终使用ARC时才应该这样做,因为否则它可能会导致错误泄漏,因为客户端不希望发布它们,因为这种方法是非常规的.

- (BOOL)runMethodThatAssignsError:(NSError * __strong *)error {
    [@[@1] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if (error) *error = [NSError errorWithDomain:@"1" code:7 userInfo:@{}];
    }];

    return NO;
}
Run Code Online (Sandbox Code Playgroud)