【问题标题】:Why does NSError need double indirection? (pointer to a pointer)为什么 NSError 需要双重间接? (指向指针的指针)
【发布时间】:2010-10-28 12:50:33
【问题描述】:

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

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

然后在doStuff中:

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

为什么上述方法不像大多数其他对象消息传递模式那样有效?为什么我们必须使用 error:(NSError **)error?

【问题讨论】:

    标签: c objective-c cocoa pointers


    【解决方案1】:

    很简单:

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

    如果你传递一个指向一个对象的指针,那么函数可以修改指针以指向另一个对象。

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

    【讨论】:

    • 这真的有助于我理解。我习惯于在区分“byref”和“byval”参数的语言上下文中考虑指针。 Byref args(即指针引用的变量)可以由被调用函数更改,调用者可以看到更改,所以我不明白@n8gray 的回答:“如果你只有一个NSError*...doStuff 会永远无法将错误对象传递回其调用者”。您的回答“函数可以修改指针以指向另一个对象”清楚地说明了如果调用者已经可以通过指针看到更改,为什么您需要**
    • 有道理,但为什么不直接修改指针指向的内容呢?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      一个老问题,但我仍然认为值得在这里提出 -

      真正的罪魁祸首是NSError。如果你查看它的类引用,它的任何属性(即域、代码或用户信息)都没有设置方法。所以没有办法,你可以只分配和初始化一个 NSError,将它传递给方法,然后在传递的 NSError 对象上填充信息。 (如果有一个 setter 方法,我们可以只传递一个 NSError * 并在方法中执行类似 error.code = 1 的操作。)

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

      【讨论】:

        【解决方案4】:

        n8gray 所说的另一种说法:

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

        【讨论】:

        • 嗨,彼得 :-) 我希望你在新的一年有一个好的开始。在更新C 关于** 时,我来到了这篇文章。对不起,我还是不太明白:如果我们只传递了一个*(一个指向对象的指针),那么对对象进行更改就足够了,对吧?为什么我们仍然需要传递指向对象的指针才能对对象进行更改?首先十分感谢。问候。
        • @Unheilig:“如果我们只传递了一个*(指向对象的指针),那么对对象进行更改就足够了,对吧?”正确的。您只需要指向对象的指针即可向该对象发送消息。 “为什么我们仍然需要传递指向对象的指针才能对对象进行更改?”因为您没有对对象进行更改;您正在创建一个新对象并将其返回给调用者。为此,您可以将其分配到调用者给您的地址——指向变量的指针,您将在其中放置指向对象的指针。
        • 嗨,@PeterHosey,我希望我不会打扰你,但在你的最后一句话中,“你可以通过在调用者给你的地址上分配它来做到这一点——指向变量的指针您将把指向对象的指针放在哪里。”我不太明白。您提到的“将指针指向对象”的指针是什么?是的,调用者给了我一个地址 (&error),它如何与我在函数内创建的新 NSError 实例 (*error = [NSError errorWithDomain:...]) 一起工作?
        【解决方案5】:

        通过阅读上面的所有答案,我仍然没有全面了解。我在下面做的外行练习,终于帮助我理解了正在发生的事情。只是把它放在那里以防它帮助其他初学者。

        假设你有关注

        @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 并进行更改。相反,您将方法接收到的 copy 中的 ZZZ 覆盖到不同的地址 YYY。 YYY 地址是一个 NSMUtableArray 对象的开始,它有一个元素 NSNUmber @(2)。原来的 ZZZ 地址仍然包含一个带有两个元素的 NSMUtableArray。 @(1) 和@(2)。所以,当方法返回时,原来的数组不受影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-17
          相关资源
          最近更新 更多