Pointers and Indirection in Objective-C

Revision as of 18:38, 13 November 2009 by Neil (Talk | contribs) (Indirection and Object Copying)

Revision as of 18:38, 13 November 2009 by Neil (Talk | contribs) (Indirection and Object Copying)

In the preceding chapters on object-oriented programming we have used, but not described a feature of Objective-C (actually derived form the underlying C programming language) in the form of pointers and indirection. A clear understanding of this topic is important when working with objects and also when passing values as arguments to methods and functions.

How Variables are Stored

When we declare a variable in Objective-C and assign a value to it we are essentially allocating a location in memory where that value is stored. Take, for example, the following variable declaration:

int myvar = 10;

When the above code is executed, a block of memory large enough to hold an integer value is reserved in memory and the value of 10 is placed at that location. When ever we reference this variable in code, we are actually using the variable value. For example, the following code adds the value of myvar (i.e. 10) to the constant value 20 to arrive at a result of 30.

int result = 20 + myvar;

Similarly, when we pass a variable through as an argument to a method or function we are actually passing the value of the variable, not the variable itself. To better understand this concept, consider the following sample program:

#import <Foundation/Foundation.h>


void myFunction(int i)
{
        i = i + 10;
}


int main (int argc, const char * argv[])
{
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        int myvar = 10;


        NSLog (@"Before call to function myvar = %i", myvar);

        myFunction (myvar);

        NSLog (@"After call to function myvar = %i", myvar);

        [pool drain];

        return 0;
}

The above program consists of a main function that declares our myvar variable and displays the current value. It then calls the function myFunction passing through the value of the myvar variable. The myFunction function adds 10 to the value it was passed as an argument and then returns to the main function where the value of myvar is once again displayed. When compiled and executed the following output is displayed:

Before call to function myvar = 10
After call to function myvar = 10

Clearly, even though the value passed through to myFunction was increased by 20 the value of myvar remained unchanged. This is because what was passed through as an argument to myFunction was the value of myvar, not the myvar variable itself. Therefore, in myFunction we were simply working on a constant value of 10 that had absolutely no connection to the origianl myvar variable.

In order to be able to work on the actual variable in the function we need to use something called indirection.

An Overview of Indirection

Indirection involves working with pointers to the location of variables and objects rather than the contents of those items. In other words, instead of working with the value stored in a variable, we work with a pointer to the memory address where the variable is located.

Pointers a are declared by prefixing the name with an asterisk (*) character. For example to declare a pointer to our myvar variable we would write the following code:

int myvar = 10;

int *myptr;

In the above example we have declared our myvar variable and then declared a variable named myptr as being of type pointer to an integer. Having declared both our variable and our pointer we now need to assign the address of our variable to the pointer. The address of a variable is referenced by prefixing it with the ampersand (&) character. We can, therefore, extend our example to assign the address of the myvar variable to the myptr variable:

int myvar = 10;
int *myptr;

myptr = &myvar;

We now have now implemented a level of indirection by creating a pointer to our variable. As such, we can now pass this pointer through as an argument to our function such that we wil be able to work on the actual variable, rather than just the value (10) of the variable. In order to access the value of a variable using a pointer to that variable, we prefix the pointer variable name with an asterisk (*). When we do this we are telling the compiler we want to work with the contents of the variable or object at the memory address contained within the pointer:

int myvar = 10;
int *myptr;

myptr = &myvar;

*myptr = *myptr + 15;

Similarly, we can modify our function to accept a pointer to an integer and perform the addition on that variable. As such, we can now modify our previous program as follows:

#import <Foundation/Foundation.h>


void myFunction(int *i)
{
        *i = *i + 10;
}


int main (int argc, const char * argv[])
{
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        int myvar = 10;
        int *myptr;

        myptr = &myvar;


        NSLog (@"Before call to function myvar = %i", myvar);

        myFunction (myptr);

        NSLog (@"After call to function myvar = %i", myvar);

        [pool drain];

        return 0;
}

Now because we are passing through a pointer to myvar when we call the function and have modified the function to work with the contents of the variable, the output clearly indicates that the function changed the value of myvar when it was called. We have, therefore, just used indirection.

Before call to function myvar = 10
After call to function myvar = 20

Indirection and Objects

So far in this chapter we have used indirection with a variable. The same concept applies for objects. In previous chapters we worked with our BankAccount class. When doing so we wrote statements similar to the following:

BankAccount *account1;

BankAccount *account1 = [[BankAccount alloc] init];

The first line of code (BankAccount *account1;) is actually declaring that the variable named account1 is a pointer to an object of type BankAccount. We are, therefore, using indirection to provide a handle to our object. The calls to the alloc and init methods subsequently create the object in memory and the assign the address of that object to the account1 pointer variable. We are, therefore, using indirection once again.

One key point to note is that we do not need to prefix the object pointer with a * when perform operations such as calling methods. For example, we can call a method on our account1 object without using an asterisk:

[account1 displayAccountInfo];

Indirection and Object Copying

The fact that our references to objects utilize indirection it is important to understand that when we use the assignment operator (=) to assign one object to another we are not actually creating a copy of the object. Instead, we are creating a second pointer to the same object:

BankAccount *account1;
BankAccount *account2;

BankAccount *account1 = [[BankAccount alloc] init];

account2 = account1;

In the above example, we will end up with two pointers (account1 and account2) that point to the same location in memory. We have not, therefore, created a copy of account1. For details on copying objects refer to the chapter entitled Copying Objects in Objective-C.