【问题标题】:Pointer issue about Objective-C objects [duplicate]关于Objective-C对象的指针问题[重复]
【发布时间】:2016-08-19 13:29:03
【问题描述】:

传递NSError 的一般使用模式是使用指向指针的指针,如下所示:

- (id)doSomethingWithObj:(NSObject *)obj error:(NSError *__autoreleasing *)error
{

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

我知道,由于传递外部error 指针的地址会分配或替换原点error,因此上述模式可以工作。

但我对NSError 或更通用的Objective-C 对象的问题是,如果我这样写,为什么会出错:

我没有使用NSError **,而是尝试了NSError *,我的想法是,由于现在参数errorNSError *的类型,所以*error应该是指针的内容(错误)指向,然后*error = [NSError errorWith...]; 可以替换*error 内容之外的原点。

但它并没有像我想的那样工作,而是弹出了上述错误。

所以让我感到困惑的是为什么*error = [NSError errorWith...]; 不适用于NSError * 的类型,但适用于NSError **

【问题讨论】:

  • 快速笔记;你需要if (error) *error = tmp;。 IE。在分配给它之前,您必须检查error 是否非nil

标签: objective-c pointers nsobject


【解决方案1】:

当您将参数传递给方法时,它会被复制,然后在您的方法中处理该副本:

- (void)changeA:(NSInteger)aToChange {
    aToChange = 7;
}

然后,当您使用该方法时,您将无法更改从该方法内部传递的值:

NSInteger a = 42;
[smb changeA:a];
// a is still 42

那是因为在 -changeA: 内部你处理了一个参数的副本,也就是说,你做了类似(伪代码)这样​​的事情:

// inside changeA:
NSInteger aToChange = a; // (value is copied)
aToChange = 7; // this does not modify a, right?

你甚至可以得到aToChange 的地址并尝试用它做点什么:

// inside changeA:
NSInteger aToChange = a; // (value is copied)
NSInteger *pointerToCOPY = &aToChange; // get address of aToChange
NSInteger *pointerToA = &a; // get address of a
// here pointerToCOPY != pointerToA

所以,回到你的代码:

NSError *error = nil; // outer error, passed as a parameter
...
- (id)doSomethingWithObj:(NSObject *)obj error:(NSError *)error
{
    // NSError *error(local copy) = error(passed argument);
    error = tmp; // here error is the pointer to the local error copy
    return nil;
}

与之前的aToChange 一样,您无法从方法内部修改外部NSError error *。这就是使用双指针的原因,您将指针发送到您的指针,这样您仍然可以访问外部 error 引用并可以设置它。

- (id)doSomethingWithObj:(NSObject *)obj error:(NSError **)error
{
    // NSError **error(local copy) = error(passed argument);
    *error = tmp; // Here *error is the pointer to the local error copy,
    // When you dereference error you get NSError *, which you can set,
    // that's your external error.
    return nil;
}

请注意,这样做是不可能的:

NSError *error = nil;
NSError *otherError = [NSError error with];
*error = otherError;

error 是一个指向结构体的指针,*error 是一个结构体,具有单个 isa 字段的结构体(从 id 类型定义的角度来看)。将指向错误的指针(otherError,即平台指针大小 int)分配给结构是不合适的。

然后,最后一个问题仍然存在,你为什么不能:

NSError *error = nil;
NSError *otherError = [NSError error with];
*error = *otherError; // why can't I assign to structs?

我不完全确定,但是,也许内存管理和 ABI 出现在游戏中,在这里它可以将一堆字节从一个对象复制到另一个对象,因为它们只是一个 C 结构。

如您所知,Objective-C 是 C 的超集,可以在那里分配结构:Assign one struct to another in C。我认为这是处理对象的 Objective-C 特性。一切都是动态的,您可以在运行时创建类并添加实例变量(Mike Ash on that topic),因此在编译时不知道结构中的isa 字段之外的内容,实际上id 是只是一个带有单个isa 的指向该结构的指针。仅仅复制单个 isa 结构并说你完成了,这不是一个好主意,事情会分崩离析。

【讨论】:

  • @Avi 是的,但是应该在编译时更早地检测到,编译器不能允许这样做,因为不可能将指向错误的指针转换为结构(目标 c 对象),它们是不兼容的类型。
猜你喜欢
  • 2017-08-10
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 1970-01-01
  • 2014-05-13
  • 2023-01-08
  • 2011-02-12
  • 1970-01-01
相关资源
最近更新 更多