2009-05-24 98 views
44

这个概念似乎困扰着我。为什么NSError对象需要将其指针传递给修改对象的方法?例如,不只是传递对错误的引用做同样的事情?为什么NSError需要双重间接? (指针指针)

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

,然后在doStuff:

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

为什么没有像大多数其他物体消息模式以上工作的工作?为什么必须改为使用错误:(NSError **)错误?

回答

75

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将永远无法传递错误对象返回给调用者。

6

的替代声明什么n8gray说:

因为你不接收对象发送消息到;你正在创建对象并将其返回。您通常需要指针指向 - NSError * - 可变参数,因为您一次只能使用return语句,而您已经使用它与NO

+0

嗨,彼得:-)我希望你在新的一年有一个好的开始。在````````````````````````````````我在这里贴了这个帖不好意思,我还是不太明白:如果我们只传递了一个`*`(一个指向对象的指针),那么就可以对对象进行更改了,对吧?为什么我们仍然需要传递一个指向对象的指针来改变对象?首先十分感谢。问候。 – Unheilig 2014-01-08 00:03:51

+3

@Unheilig:“如果我们只传递一个`*`(指向一个对象的指针),那就足以对对象进行更改了,对吗?”对。您只需要指向该对象的指针即可将消息发送到该对象。 “为什么我们仍然需要传递一个指向对象的指针来改变对象呢?”因为你没有对对象进行更改;你正在创建一个新对象并将其返回给调用者。你可以通过在调用者给你的地址分配它 - 指向变量的指针,在那里你将把指针指向对象。 – 2014-01-08 03:41:15

89

很简单:

如果你传递一个指向对象的功能,该功能只能修改什么指针指向。

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

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

6

一个老问题,但我仍然认为它的价值将这一在这里 -

实际的罪魁祸首是NSError。如果您查看其类引用,则不会为其任何属性设置setter方法,即domain,code或userInfo。所以没有办法,你可以只分配和初始化一个NSError,将它传递给该方法,然后在传递的NSError对象上填充信息。 (如果有一个setter方法,我们可以通过一个NSError *并在方法中执行类似error.code = 1的操作。)

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

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。由于数组包含一个地址(即一个指针),拷贝的特殊之处在于收到的是地址为ZZZ的另一个变量。所以,如果methodX确实[array removeObjectAtIndex:0],那么从地址ZZZ开始的对象会受到影响(现在只包含一个NSNUmber @(2))。所以,当方法返回时,原始数组也会受到影响。

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