5

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

NSError* error = nil;
[self invokeAsync:^id{
    return [self doSomething:&error];
}];

我在第三行收到一个编译器错误,告诉我:

将“NSError *const__strong *”发送到“NSError *__autoreleasing *”类型的参数会更改指针的保留/释放属性

这是为什么?

4

2 回答 2

20

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

但是,没关系,因为该代码没有意义。

异步调用不能在调用线程的堆栈中设置状态。即没有办法error可以设置为有意义的值。

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


如果你想异步调用一些错误,你需要一个回调:

[self invokeAsync:^id{
    NSError *e;
    if ([self doSomething:&e])
        [self errorHappened:e];
    else
        [self asyncThingyDone];
}];
于 2013-10-31T08:21:56.373 回答
3

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

另一个可能是您所要求的,即为什么编译器会给出这样的错误。同样正如@bbum 所说,“编译器消息令人困惑”。为了将第二个问题与第一个问题分离,我们假设您的电话invokeSyncAndWait:invokeAsync:. 现在时间问题已经解决了,我们可以专注于第二个问题:

NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];

块中捕获的error变量只是按值复制,而不是真正的引用:

封闭词法范围的局部堆栈(非静态)变量被捕获为 const 变量。它们的值取自程序中块表达式的位置。在嵌套块中,值是从最近的封闭范围捕获的。

因为您没有error对块中变量的真正引用,所以您无法获取它的地址。这就是编译器拒绝编译您的代码的原因。

要获取引用变量,您应该使用__block

使用 __block 存储修饰符声明的封闭词法范围的局部变量由引用提供,因此是可变的。任何更改都反映在封闭词法范围中,包括在同一封闭词法范围内定义的任何其他块。这些在“__block 存储类型”中有更详细的讨论。</p>

所以工作代码是:

__block NSError* error = nil;
[self invokeSyncAndWait:^id{
    return [self doSomething:&error];
}];

虽然它仍然是危险的代码:

因此,__block 变量的地址会随着时间而改变。

我只是解释了编译问题。

于 2013-10-31T19:25:28.370 回答