【问题标题】:Cannot understand NSError/NSObject pointer passing behavior无法理解 NSError/NSObject 指针传递行为
【发布时间】:2016-12-26 12:38:48
【问题描述】:

尽管我已经阅读了 Why does NSError need double indirection? (pointer to a pointer)NSError * vs NSError ** 等等,但我现在对指针的指针感到困惑。

我已经做了一些思考,但仍有一些问题。

这是我写的:

NSError *error = [NSError errorWithDomain:@"before" code:0 userInfo:nil];
NSLog(@"outside error address: %p", &error];
[self doSomethingWithObj:nil error:&error];

为了测试上面的NSError方法,我写了这个:

- (id)doSomethingWithObj:(NSObject *)obj error:(NSError *__autoreleasing *)error
{
    NSLog(@"inside error address: %p", error);
    id object = obj;
    if (object != nil)
    {
        return object;
    }
    else
    {
        NSError *tmp = [NSError errorWithDomain:@"after" code:0 userInfo:nil];
        *error = tmp;
        return nil;
    }
}

但是我发现这两个日志地址是不同的。这是为什么呢?

2016-08-19 19:00:16.582 Test[4548:339654] outside error address: 0x7fff5b3e6a58
2016-08-19 19:00:16.583 Test[4548:339654] inside error address: 0x7fff5b3e6a50

他们不应该是一样的,因为那只是一个简单的价值副本吗?如果它们应该不同,指向指针的指针如何最终指向同一个NSError 实例?

【问题讨论】:

  • 编译器必须对指向指针的指针做一些事情,也许是为了处理 ARC。在最新版本的 Objective-C 中,指针得到了太多的聪明,所以以前简单的值复制可能不再那么简单了。您可能希望以与 NSError 无关的方式重新表述这个问题 - 例如,“指针在作为方法参数传递时发生变化”或类似的内容。
  • 但是 NSError* __autoreleasing* 是一种在任何地方都可以使用的模式,编译器会以不同的方式识别和处理它。

标签: ios objective-c pointers nserror


【解决方案1】:

调用者中的变量类型为NSError*。地址的类型为NSError* *。函数期望NSError* __autoreleasing *。因此编译器会创建一个NSError* __autoreleasing 类型的隐藏变量,在调用前将NSError* 复制到隐藏变量中,并在调用后将其复制回来以获得__autoreleasing 的语义。

【讨论】:

  • 优秀的答案!我真的忘记了 clang 文档中有类似的东西。你能推荐一些关于细节的东西吗?
  • @gnasher729 我不确定原因是否与__autoreleasing 有关,因为我再次测试了它:删除关键字__autoreleasing 后,我遇到了同样的问题——内部地址不同和外部指针。
【解决方案2】:

因此,在第一行初始化之后,error 是一个指向 NSError 对象的指针。

在第一个日志中,您记录的是 指针 所在的地址。这就是 & 地址操作符的效果。无论如何,就是将地址传递给 doSomething 方法。

你正在传入:指针 -> 指针 -> nserror-object。

但请注意 doSomething 签名中的 双重间接。自动释放注解使其难以被发现,但它的 NSError **.

因此编译器会获取您的参数并“解包”两次。

它从指针 -> 指针 -> nserror-object 开始。然后在第一次间接之后它变成指针-> nserror-object。然后在第二次间接之后它变成 nserror-object。

Ergo,您正在记录两件不同的事情。第一个是指向 nserror-object 的指针的地址。第二个是nserror-object本身的地址。

编辑: @MANIAK_dobrii 指出 error 指向的对象在前后情况下本身不同。

确实如此。如果在 doSomething 中发生错误,那么它会在 else 子句中创建一个全新的 NSError 实例。然后它将其加载回error 指针。这就是为什么您会看到两个不同的地址,之后 error 指针完全指向另一个对象。

【讨论】:

  • 不是这样,error 已经是一个指向 NSError 实例的指针,&error 是指向那个指针的指针。问题是在方法调用之前和之后它们是不同的。我也不知道为什么,我想它们是一样的。
  • @MANIAK_dobrii 是的,我是说'error 已经是指向 NSError 实例的指针,&error 是指向该指针的指针'。但我想我明白你的意思。是的,它们是不同的,因为在该方法中,另一个 NSError 实例在 else 块中启动并加载到指针中。这是一个不同的对象。
  • 也不是这样。 @gnasher729 给出了准确的答案。那是 ARC 搞砸了,我记得在一些关于 __autoreleasing 的双指针的 clang 文档中读到了这一点,所以看看。另外,对不起,我在“之后(方法调用)”中不清楚,我的意思是在调用内部的方法调用之后,即在调用期间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多