关于Objective-C对象的指针问题

时间:2016-08-19 13:29:03

标签: objective-c pointers nsobject

传递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个对象的问题是,如果我写这样的话,为什么会出错:

enter image description here

我没有使用NSError **,而是尝试NSError *,我想的是,因为现在参数errorNSError *的类型,所以{{ 1}}应该是指针(错误)指向的内容,然后*error可以替换*error = [NSError errorWith...];内容之外的来源。

但它没有像我想的那样工作,但却出现了上述错误。

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

1 个答案:

答案 0 :(得分: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类型defenition的角度来看)。将指向错误(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。我认为,目标C的特性处理对象。一切都非常动态,您可以在运行时创建类并添加实例变量(Mike Ash on that topic),因此在编译时不知道结构中isa字段之外的是什么,实际上id只是指向具有单个isa的结构的指针。仅仅复制单个isa结构并说你已经完成并不是一个好主意,事情会崩溃。