使用指向块外部声明的对象的指针

时间:2013-10-31 06:14:28

标签: objective-c pointers compiler-errors objective-c-blocks

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

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

我在第三行收到编译错误告诉我:

  

发送' NSError * const__strong *'到类型' NSError的参数   * __ autoreleasing *'更改指针的保留/释放属性

为什么?

2 个答案:

答案 0 :(得分:20)

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

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

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

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


如果要异步调用某个错误,则需要回调:

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

答案 1 :(得分:3)

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

另一个可能是你要求的,也就是编译器给出这样一个错误的原因。另外正如@bbum所说,“编译器消息令人困惑”。为了将第二个问题与第一个问题解耦,我们假设您的呼叫是invokeSyncAndWait:而不是invokeAsync:。现在时间问题已经消失,我们可以关注第二个问题:

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

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

  

封闭词法范围本地的堆栈(非静态)变量是   捕获为const变量。他们的价值取决于   程序中的块表达式。在嵌套块中,值   是从最近的封闭范围捕获的。

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

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

  

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

所以工作代码是:

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

但它仍然是危险的代码:

  

因此,__block变量的地址可能随时间而变化。

我刚刚解释了编译问题。