Objective c 使用指向块外部、块内部声明的对象的指针

Objective c 使用指向块外部、块内部声明的对象的指针,objective-c,pointers,compiler-errors,objective-c-blocks,Objective C,Pointers,Compiler Errors,Objective C Blocks,我试图使用Objective-C块外部声明的对象的指针,在块本身内部。例如: NSError* error = nil; [self invokeAsync:^id{ return [self doSomething:&error]; }]; 我在第三行得到一个编译器错误,告诉我: 正在将“NSError*const\uu strong*”发送到“NSError”类型的参数 *__自动释放*'更改保留/释放指针的属性 这是为什么?编译器消息令人困惑,但它告诉您类型不匹配 但是,

我试图使用Objective-C块外部声明的对象的指针,在块本身内部。例如:

NSError* error = nil;
[self invokeAsync:^id{
    return [self doSomething:&error];
}];
我在第三行得到一个编译器错误,告诉我:

正在将“NSError*const\uu strong*”发送到“NSError”类型的参数 *__自动释放*'更改保留/释放指针的属性


这是为什么?

编译器消息令人困惑,但它告诉您类型不匹配

但是,没关系,因为那个代码毫无意义

异步调用无法在调用线程的堆栈中设置状态。即,
错误
无法设置为有意义的值

也就是说,方法
invokeAsync:
将在执行工作块之前返回。因此,无法从
invokeAasync:
返回任何有意义的内容来指示块执行的成功/失败


如果要在出现错误的情况下异步调用某个对象,则需要回调:

[self invokeAsync:^id{
    NSError *e;
    if ([self doSomething:&e])
        [self errorHappened:e];
    else
        [self asyncThingyDone];
}];

这里有两个问题。第一个是@bbum已经指出的时间问题

另一个可能是您所要求的,即为什么编译器会给出这样的错误。正如@bbum所说,“编译器消息令人困惑”。为了将第二个问题与第一个问题解耦,我们假设您的调用是
invokeSyncAndWait:
,而不是
invokeAsync:
。现在时机问题已经过去,我们可以关注第二个问题:

NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];
块中捕获的
错误
变量只是一个按值拷贝,而不是实际引用:

包含词法作用域的堆栈(非静态)局部变量为 捕获为常量变量。它们的值是在 程序中的块表达式。在嵌套块中,值 从最近的封闭范围捕获

由于块中没有对变量
error
的实际引用,因此无法获取该变量的地址。这就是编译器拒绝编译代码的原因

要获取引用变量,应使用
\uu块

使用声明的封闭词法作用域的局部变量 __块存储修改器是通过引用提供的,因此是可变的。任何更改都会反映在封闭的词法范围中,包括 在同一封闭词法范围内定义的任何其他块。 这些将在“块存储类型”中进行更详细的讨论

因此,工作代码是:

__block NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];
但这仍然是危险的代码:

因此,块变量的地址可以随时间而改变


我刚刚解释了编译问题。

请注意clang中的ARC写回错误功能,这会导致
错误
变量为零,即使发生错误。