You know, without pointers Objective-C is just Swift with semicolons.
Suppose you need to allocate a region of memory for your own use. You can store integers there, or an image, or a dictionary of objects or a sound clip. The pointer is your handle on that piece of memory. It tells you primarily the location of the region of memory you got, but in Objective-C, it also keeps track of e.g. a reference count so that you do not need to worry about allocating and releasing it. So things can get pretty complex pretty quick.
Let us assume the simplest possible scenario. You need some memory to store an object. Say a string, but that does not matter. Also, to all the purists out there, this is a general discussion not meant to be technically correct, but to illustrate the idea to OP.
First you will alloc that object
NSString * p = [NSString alloc];
This will ask the OS for a region of memory of the correct size to store a string and the OS will respond with a pointer that points to the newly allocated area, that address now stored in p. This is just an area of memory and you can do what you want with it. You need not even store a string there but things can go pear shaped pretty quick if you wander off like that.
Now the OS just gave you a region of memory. That memory is not blank, it holds some left over bits from whatever it was used for before. So your first task is to clear the memory - to initialise it. So the next step is to initialise it
p = [p init];
After this the area of memory has been prepared by some initialiser, e.g. it was zeroed or loaded some default values into the area of memory for a more complex class.
Note in reality this is a bit different - see the edit below.
In C you have alloc and malloc and calloc to do this. Some of these will just allocate memory and others will allocate the memory and set it all to 0 - clearing all the bits. In Objective-C you typically do this as a single step
p = [[NSString alloc] init];
Since the OS allocated a region of memory you need to tell it when you are done to free it, e.g.
[p release];
or in C you'd use free when done.
Now this is just academic, as ARC will do this automatically for you by keeping an internal reference counter to the object to determine when it goes out of scope and then to release it, but I digress. Let us just for now assume you need not worry about releasing that block of memory back to the OS, ARC will take care of it for you.
Let us also for now assume a more complex class, say
p = [[Invoice alloc] init];
Then when you e.g. do
p.items = 100;
the compiler will know that it needs to copy the integer value of 100 to the correct place inside that block of memory and it will do so for you. There are many variations on this theme, but basically as you assign values to ivars those values gets copied into the allocated area of memory and as you read them they get copied back from the correct spot into the local variable as well.
On the other hand, if you assign a value to a simple int variable
int items = 100;
this is not copied to your pre-allocated area. In stead, the message or function has a special local working area called a stack where these variables are created and later discarded dynamically. So the statement above will load the value 100 onto the stack and will note the position so that later if you refer to items it will know where to find and read or write its value on the stack.
Likewise
Invoice * p = [[Invoice alloc] init];
is loaded on the stack, but here the pointer's value or memory address is loaded on the stack, not the contents it points to. The fact that it is a pointer indicates to you that it points to some region of interest somewhere else.
If later you do
Invoice * q = p;
you load (on the stack) the same pointer value of p into q, and now p and q both point to the same area of memory where the actual object is stored. So pointers add this one layer of separation between the value of the pointer that can be interpreted as pointing to an address in memory, and the data that is stored at this address. This can go on, e.g. you can and do get pointers to pointers and so on.
Since you can not allocate memory yourself on the dynamic stack, you can not store objects on the stack, but you can store pointers to objects there.
Pointers give you incredible power as you access memory directly. You can e.g. write a general function to allocate memory or to write memory to disk or to clear memory or to transfer a block of memory through the network or to load a block of memory to a screen area. Modern compilers take care of these things for us so it does not sound like such a big deal, but the powerful thing is that you use the same routine for all your objects. If your array stores pointers then you can store anything in the array as another example.
Pointers also outlive the stack. The stuff you put on the stack e.g.
int items = 100;
only survives as long as you are inside the function. The stack is discarded when the function returns so you need a specially allocated area of memory to communicate in a way that transcends functions and sometimes even applications.
Here is a deeper example, using only integer pointers, not Objective-C object ones, to further illustrate the difference between pointers and values on the stack.
In C you'd do
int * p = malloc ( 10 * sizeof( int ) );
which will allocate enough space to store 10 integers and return the pointer to the allocated space in p. p is now on the stack and contains the address of the area of memory just returned.
Next if you do
int q = 123;
the value 123 is stored onto the stack and under the moniker q which the compiler will translate to this specific location on the stack as you work with q e.g. assign a value to it.
Next, if you do
* p = q;
* ( p + 1 ) = q / 2;
this will load the value of q (123) from the stack and into the area of memory pointed to by p, which points to the first of the 10 integers. Next you calculate q / 2 and store that value into the location of the next integer, the 1 after the first, pointed to by p and referenced by * ( p + 1 ). The compiler can do this easily as it knows the size of an integer so it knows how to translate
* ( p + 1 )
into the correct area that points to the integer shifted 1 away from the base p.
This is another topic with pointers, it allows for incredibly efficient pointer arithmetic that you can use to traverse areas of memory. Also, this is why sizeof is such an important keyword in C as it is directly linked to how the language handles memory through pointers.
In C, when done, you need to release the area back to the OS so you'd do
free( p );
when done.
This seems simple enough, but in practise it is difficult to keep track of all the allocated regions of memory. It easily happens that you do not release one or that you load data into an area already released. The former leaks memory and the latter can have disasterous consequences.
This is why Objective-C's ARC represents such a big step in the development of the language as it takes care of this for you. In the ARC world the allocation and freeing of memory, a BIG topic in pointers, is no longer that important or visible - so it can also shield some understanding of pointers from its users.
In e.g. Swift this allocation and freeing happens completely behind the scenes and you do not really worry about allocating or freeing memory directly. This makes it difficult to understand the difference between pointing to memory and the memory being pointed to, which is at the heart of pointers.
You know, without pointers Objective-C is just Swift with semicolons.
I started and now ended with this somewhat controversial statement.
Why?
When I was a programmer struggling with the concept of pointers, I had a mind to give it up completely. This was before objects and you could (try to) do all you needed to on the stack and side step pointers that way.
I just came from Pascal (where you do not use pointers that much) and struggled with C, especially with pointers, and then read somewhere that, without pointers, C is just Pascal with curly braces! I knew C was powerful, more so than Pascal, but to grasp for that I had to master pointers.
Note - as mentioned by Sulthan, Pascal also has pointers and it is very debatable if one is more powerful than the other.
Here is a nice sample to illustrate a lot of these concepts, also how you can use pointers to iterate through memory.
int main(int argc, const char * argv[]) {
@autoreleasepool
{
// insert code here...
int a = 10; // a loaded on stack
int * b = & a; // b now points to a
NSLog ( @"a pointer %p value %d", & a, a );
NSLog ( @"b value %p points to %d", b, * b );
int * p = malloc ( 10 * sizeof ( int ) );
int * q = p;
// Show p on the stack and * p allocated somewhere
// Note the values, & p agrees with & a of earlier e.g.
NSLog ( @"p address %p on stack but points to %p", & p, p );
for ( int i = 0; i < 10; i ++ )
{
* q = i; // loads value i into area pointed to by q
NSLog ( @"i now %d", i );
// Note the huge difference between addresses on the stack and addresses alloc'd
NSLog ( @"\tp %p p + i %p value pointed to is %d", p, p + i, * ( p + i ) );
NSLog ( @"\tq %p value pointed to is %d", q, * q );
q ++; // Increment pointer, not value, now points to next int
}
// Of course ...
free ( p );
}
return 0;
}
EDIT Objective-C alloc
I mention in the text that the memory needs to be initialised after an Objective-C alloc. I could not find any documentation now, but I am pretty sure that Objective-C's alloc, e.g. something like
NSString * s = [NSString alloc];
will in fact both allocate and clear or zero the memory. The init is not really meant to zero but for the class to initialise its members properly.
But, since we are discussing pointers and since you are looking for programming examples, what better than to write a piece of code to test this.
The code below will create two Objective-C classes of your choice. One will only be alloc'd and one will be alloc'd and init'd. Then it compares these two classes byte for byte using pointers to see if there is a difference. Another nice example of using pointers.
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// The object we will test
#define TEST_OBJECT NSFileManager
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Allocate some complex class
TEST_OBJECT * obja = [TEST_OBJECT alloc];
TEST_OBJECT * obji = [[TEST_OBJECT alloc] init];
// Size of this class
unsigned long n = class_getInstanceSize ( TEST_OBJECT.class );
NSLog ( @"Object size is %lu", n );
NSLog ( @"\tLocation in memory %p", obja );
NSLog ( @"\t %p", obji );
// Get pointers to the two instances
void * p = ( __bridge void * ) obja;
void * q = ( __bridge void * ) obji;
// Compare the two
int cmp = memcmp( p, q, n );
NSLog ( @"Result is %d - %@", cmp, cmp ? @"different" : @"same" );
// Dump the actual bytes side by side
for ( int i = 0; i < n; i ++ )
{
int l = ( unsigned ) ( * ( ( unsigned char * ) p + i ) ); // Left
int r = ( unsigned ) ( * ( ( unsigned char * ) q + i ) ); // Right
NSLog ( @"Byte %3d | %3d | %3d | %@", i, l, r, l != r ? @"***" : @"" );
}
}
return 0;
}