简体   繁体   中英

What are difference between *a and **a in objective-c object?

I write the following code:

NSArray *array = @[@1, @2];

How to output *array and **array , and what is the difference between them?

There are some answers, but I think none of them are a real help to you, because they describe the technical meaning. Let's have a look to the first declaration:

NSArray *array …;

When somebody talks about this code, you will find statements like array is an instance object of NSArray . Even every experienced Objective-C developer knows the real meaning of this statement, it is simply wrong. What is the correct statement?

A. The instance object of NSArray

An instance object has a state, basically a set of data stored. To do so, it needs memory, which is reserved while object creation. You do not deal directly with that, but it is done inside +alloc . This is done explicitly at runtime while the program is running ("on heap", "heap allocation").

… [NSArray alloc] … // Get memory for an instance object of type NSArray

You address such an object solely via its address, the number of the first memory cell of the occupied memory area. (Every memory cell has a number, called the address. Yes, it is similar to addressing inhabitants in a house via the number of the house in a street. Therefore you can imagine the memory as a very, very long street.)

But an identifier like array only exists at compile time and is removed when the program is compiled. Therefore it is obvious that an identifier like array never denotes an instance object.

Short version: An instane object is an area of memory and is solely addressed via the number of the first memory cell (location).

B. A pointer to an instance object (reference)

But if an instance object is addressed via its number at runtime, how can my code deal with it? The trick is that the number is stored in a variable. (Looking to the C standard that is not completely correct. They say that it is stored in an object. But these objects has nothing to do with Objective-C objects and I will focus on variables, a subtype of objects.)

So you can have a variable storing the memory location of an instance object. Such a variable is called a pointer variable. It is declared with an extra * . So

NSArray * array;

means: A pointer variable with the identifier array that stores the location of an instance object of the type NSArray .

(Addresses are numbers. They are integral numbers. Therefore there is a connection between pointer variables and integers. And you can apply calculations to that numbers, called "pointer arithmetics". In some Situations this is important for C developers, but not for you as an Objective-C developer.)

The memory for this variable is not reserved explicitly with +alloc , but implicitly when you enter the area of your code, where the variable is declared. (Not completely correct again, but enough for this explanation.) So let's have a look again to a very boiled down version of object creation:

- (void)method
{
  NSArray *array = [NSArray alloc];
}

The right side of this statement reserved memory for the object instance and returns a number, the address of the memory area. This number is assigned to a reference called array . The memory for that reference (it stores something, so it needs memory) is reserved implicitly via its definition.

Pointers to objects are usually called references.

Short version: So array is a reference to an instance object, storing the address of the instance object.

C. A pointer to a pointer to an instance object

Okay, we have an instance object that occupies memory to store the object state, addressed via its memory location (address). Then we have a variable that stores that address, the reference array . You can address that via its identifier.

But sometimes it is useful – I will have an example below – to address the reference variable via its address, too. You can get the address of a variable using the address operator & .

&array

What you get is: The address of a variable storing the address of an instance object. The type of such a double indirection ("address of … address of …") is

NSArray ** array;

This is, because in a variable definition the * means "address of".

Short version: A pointer to a reference is a variable that stores the address of a variable that stores the address of an instance object. It is declared with ** . (Yes, you can have more level of indirections … No, that is not easier to understand.)

D. Use cases for pointers to references

Usually you do not need such double indirections in Objective-C. But there is one important use case, an error out parameter. To understand that, we will look at a method with a single indirected parameter as you know is:

- (BOOL)methodThatCanProduceAnError:(NSError*)error
{
   …
   error = [NSError alloc] … // Create an error object and store its address to the reference variable error.
   return NO;
   …
}

This method should emit an error via its error parameter.

You "call" such a method with code like that:

…
NSError *error; // A reference variable pointing to an instance object of type NSError
error = nil; // I do not have an error, so it points to "nothing".
[anInstance methodThatCanProduceAnError:error];

What happens? You pass the address of an instance object to that method as an argument. You pass nil for saying "I have no error". This is quite clear, because the method should pass out a reference to an instance object.

So the interesting part is inside the method, when it creates the error object and tries to pass it out. This does not work!

In Objective-C arguments are always passed by value. That means, that the value of the argument in the "calling code" is taken and assigned to a new variable inside the "called code". This new variable is called a parameter variable. When the method tries to change the value of the variable

error = [NSError alloc] … // Create an error object and store its address to the reference variable error.

it solely changes the value of the variable copy inside the method. The new reference will never find the way out of the method and the variable error in the calling code is left untouched. In the "calling code", error still has its old nil value.

So we need a way to change the content of the reference inside the "calling code". We do that by passing the address of the reference to the method:

- (BOOL)methodThatCanProduceAnError:(NSError**)error // double indirection
{
   …
   *error = [NSError alloc] … // *error is a reference to an object
   return NO;
   …
}

First in the method head we declare a parameter variable that is double indirected: The address of a reference. Second we assign the address of an instance object to the location, error points to. (This is done by the * .) The assignment is not done to the parameter variable, but to the location, the parameter variable points to.

Therefore we can pass the address of the reference:

NSError *error; // A reference variable pointing to an instance object of type NSError
error = nil; // I do not have an error, so it points to "nothing".
NSError ** pointerToError = &error; // The address of the reference.
[anInstance methodThatCanProduceAnError:pointerToError];

Now the method changes the contents of the reference variable. The error is passed out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM