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

Call Method that assigns NSError in Block Crashes

我想了解为什么会因 EXC_BAD_ACCESS 错误而崩溃。它 returns 从方法调用正常,但随后在 [self runMethodThatAssignsError:&error] 上立即崩溃。

我有 found a similar post here,但它没有解释发生了什么,而且相当陈旧。

- (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;
}

运行 您在 Instruments 中的示例代码,似乎 -[NSArray enumerateObjectsUsingBlock:] 正在将其块包装在自动释放池中。由于 NSError ** 指针在默认情况下被隐式假定为 __autoreleasing,您的 NSError 对象在分配给 *error 时自动释放,因此被 [=12= 回收]的自动释放池。

有两种方法可以解决这个问题。第一个是在块外使用局部变量,使 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;
}

或者,您可以只将 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;
}