Unheilig
Unheilig

Reputation: 16302

C: Double Pointers and Objective-C NSError Double Pointers Usage

I happened to stumble upon posts here on SO regarding the double star (**) usage with NSError. Decided to dig it a little deeper, which led me revisiting C.

I have already read many posts here (not a duplicate here) on NSError and I understand why they do so.

So, I decided to write a little snippet in the IDE (Xcode) to see it myself in action.

int a = 10;
int b = 20;

int *c = &a;
int *d = &b;

int **e = &d;

NSLog(@"Before: a is: %d b is: %d", a, b);

[self testSingleStar:c]; 
[self testDoubleStar:e]; 

NSLog(@"After: a is: %d b is: %d", a, b);

Methods:

- (void)testDoubleStar:(int**)x
{
    int anotherValue1 = 22;
    **x = anotherValue1;
}

- (void)testSingleStar:(int*)x
{
    int anotherValue2 = 33;
    *x = anotherValue2;
}

Output:

Before: a is: 10 b is: 20

After: a is: 33 b is: 22

Both values of a (via a single pointer) and b (via a double pointer) did get changed.

So, if we can change the value (of a) using only a single pointer, why would we need to use a pointer to a pointer to NSError so that we could modify the NSError object in method call where NSError object is passed in its parameter (&error)?

In addition:

In regards to the above, passing something to the parameter in C is actually passing a copy to the parameter (even in the case of a single pointer only a copy of it will get passed), so anything change made within the scope of the method will not effect the original value passed to it (that's why they use a double star).

But the above sample, in which only a single pointer is used, seems to contradicts this because the value (namely a pointed to by a single pointer - c) did get changed?

Upvotes: 1

Views: 1113

Answers (2)

Grady Player
Grady Player

Reputation: 14549

This is because you can't just have an NSError object... you can only deal with Objective-C objects by pointer... so while you could theoretically take a pointer to NSError and swap its contents... it would mess up encapsulation (other things could be relying on that data that used to be there.).

also just reassigning the pointer to a new value doesn't work, because it doesn't affect the callers version.

so while it seems tempting to:

-(void)method:(NSError *)errObject
{
    //something like
    errObject = [NSError errorWithBlah:@"blah"]; // this will only change the local variable errObject, not the callers version, that pointer is just copied by value. 
}

or

 /// not real code, didn't check to see if my params are in the correct places etc.
-(void)mangler:(NSError *)errObject
{
    //something like
    errObject = realloc(errObject,sizeof *errorObject);
    memcpy(errObject,[NSError new],sizeof NSError);

    // you have killed the old version that may be in use.
}

Upvotes: 2

Gwendal Roué
Gwendal Roué

Reputation: 4044

You can modify an external variable of type foo by providing a foo * to a function or method:

+ (void)doJobReturningInt(int *) { ... }

int a;
[MyClass doJobReturningInt:&a]; // may modify the value of the variable a;

So far, so good.

Now how do we declare a variable storing an NSError object? With the NSError * type:

NSError *error;

So if we want to modify it, we need to provide a pointer to its type, that is to say NSError **.

+ (void)doJobReturningError:(NSError **)outError { ... }

NSError *error;
[MyClass doJobReturningError:&error]; // may modify the value of the variable error;

See? This is the same situation: for modifying the type foo, provide a foo*.

Upvotes: 4

Related Questions