使用ARC在块中设置NSError

Wes*_*Wes 10 objective-c nserror objective-c-blocks automatic-ref-counting

我希望使用自动引用计数从项目的块中设置NSError指针.以下是我的代码的简化版本:

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        [blockSelf doSomethingWithItem:item error:error];
    }];
}
Run Code Online (Sandbox Code Playgroud)

这个编译但是给定error可以修改 doSomethingWithItem我尝试为要修改的块创建一个本地NSError,然后用于error在枚举之后设置原始(我没有显示):

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    __block NSError *blockError = nil;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        [blockSelf doSomethingWithItem:item error:&blockError];
    }];
}
Run Code Online (Sandbox Code Playgroud)

无法编译时出现以下错误:

将非本地对象的地址传递给__autoreleasing参数以进行回写

谷歌搜索此错误只返回Clang源代码本身的结果.

一个看起来有效但有点难看的解决方案是有一个内部和外部错误指针:

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    __block NSError *outerError = nil;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        NSError *innerError = nil;
        [blockSelf doSomethingWithItem:item error:&innerError];
        outerError = innerError;
    }];
}
Run Code Online (Sandbox Code Playgroud)

在块中设置NSError的正确方法是什么?

jtb*_*des 8

试试这个:

// ...
__block NSError *blockError = nil;
[items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
    NSError *localError = nil;
    if (![blockSelf doSomethingWithItem:item error:&localError]) {
        blockError = localError;
    }
}];
// ...
Run Code Online (Sandbox Code Playgroud)

至于为什么这是必要的,我仍然试图自己掌握它.当我这样做时,我会更新这个答案.:)

  • 本质上,当您创建__block变量时,编译器会发出代码以在运行时为该变量创建堆分配的副本,然后在调用作用域和块之间共享该副本.然后它重写对这些变量的所有引用以访问该内存.这就是__block变量可以"超过"原始范围的方式.一旦你知道这种情况,就很容易理解为什么针对原始堆栈变量的编译时地址操作不会影响堆分配的副本.这略微过度简化了,但这是为什么这是必要的关键. (3认同)