为什么NSError需要双重间接? (指向指针的指针)

时间:2009-05-24 04:53:34

标签: c objective-c cocoa pointers

这个概念似乎让我感到困扰。为什么NSError对象需要将其指针传递给正在修改对象的方法?例如,不只是传递对错误的引用做同样的事情吗?

NSError *anError;
[myObjc doStuff:withAnotherObj error:error];

然后在doStuff中:

 - (void)doStuff:(id)withAnotherObjc error:(NSError *)error 
 {
    // something went bad!
    [error doSomethingToTheObject];
 }

为什么上述工作不能像大多数其他对象消息传递模式一样工作?为什么必须使用错误:(NSError **)错误?

5 个答案:

答案 0 :(得分:93)

很简单:

如果将指向对象的指针传递给函数,该函数只能修改指针指向的内容。

如果将指针传递给指向对象的指针,则该函数可以修改指针以指向另一个对象。

在NSError的情况下,该函数可能想要创建一个新的NSError对象并向您传回指向该NSError对象的指针。因此,您需要双重间接,以便可以修改指针。

答案 1 :(得分:77)

当方法通常返回某个值但是如果失败则可能需要返回错误对象(类型为NSError**)时使用NSError*模式。在Objective-C中,方法只能返回一种类型的对象,但是这种情况下你想要返回两种。在需要返回额外值的C语言中,您需要指向该类型值的指针,因此要返回NSError*,您需要NSError**参数。一个更现实的例子就是:

// The method should return something, because otherwise it could just return
// NSError* directly and the error argument wouldn't be necessary
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error
{
  NSArray *result = ...;  // Do some work that might fail
  if (result != nil) {
    return result;
  } else {
    // Something went bad!
    // The caller might pass NULL for `error` if they don't care about
    // the result, so check for NULL before dereferencing it
    if (error != NULL) {
      *error = [NSError errorWithDomain:...];
    }
    return nil;  // The caller knows to check error if I return nil
  }
}

如果您只有NSError*参数而不是NSError**,那么doStuff永远无法将错误对象传递回其调用者。

答案 2 :(得分:8)

一个老问题,但我认为值得把它放在这里 -

实际罪魁祸首是NSError。如果查看其类引用,则不存在任何属性的setter方法,即域,代码或userInfo。所以没有办法,您可以只分配和初始化NSError,将其传递给方法,然后填充传递的NSError对象的信息。 (如果有一个setter方法,我们可能刚刚传递了一个NSError *并在方法中做了类似error.code = 1的事情。)

因此,如果出现错误,您必须在方法中生成一个新的NSError对象,如果您这样做,将其传递回调用者的唯一方法是使用NSError **参数。 (由于上述答案中解释的原因。)

答案 3 :(得分:6)

关于n8gray所说的替代声明:

因为您没有收到要发送消息的对象;你正在创建对象并返回它。您通常需要指针指向 - NSError * - 变量参数,因为您一次只能对一件事使用return语句,并且您已经在NO使用它

答案 4 :(得分:0)

通过阅读上面的所有答案,我仍然无法全面了解情况。我在下面做的外行练习,终于让我明白了发生了什么。只是把它放在那里,以防其他初学者。

假设你有以下

@interface Class X
-(void) methodX:(NSMutableArray *)array;
@end

在代码的其他部分,您有以下顺序

ClassX *objectX = [[ClassX alloc] init];
NSMutableArray *arrayXX = [@[@(1), @(2)] mutableCopy]; 
//What is stored in arrayXX is the address in the heap at which the NSMutableArray object starts, lets call this address ZZZ
//array starting at address ZZZ in the heap now contains NSNUmbers @1,@2
[objectX methodX:array]

当您调用[objectX methodX:array]时,该方法收到的内容是array副本。由于数组包含一个地址(即指针), copy 的特殊之处在于接收的是另一个地址为ZZZ的变量。

因此,如果methodX执行[array removeObjectAtIndex:0],那么从地址ZZZ开始的对象会受到影响(现在只包含一个NSNUmber @(2))。因此,当方法返回时,原始数组也会受到影响。

假设methodX确实array = [@[@(2)] mutableCopy];,那么原始数组不会受到影响。这是因为你没有进入地址ZZZ并改变了一些东西。相反,您将方法接收的副本中的ZZZ覆盖到另一个地址YYY。 YYY地址是NSMUtableArray对象的开始,其中一个元素为NSNUmber @(2)。原始ZZZ地址仍包含带有两个元素的NSMUtableArray。 @(1)和@(2)。因此,当方法返回时,原始数组不受影响。