Understanding Pointers and Equality
When most people think of a pointer, they think of an arrow, a sign, or even a human finger pointing in a certain direction. This is exactly what a pointer is in C or Objective-C: an indicator pointing at another location.
When your code executes, that code resides in memory. Available to your code are two types of memory: the stack and the heap. The stack, as its name implies, is a contiguous block of memory. Values can be pushed into the stack and they can be popped off of the stack. This mechanism is how functions are called: The parameters to a function are pushed onto the stack and then execution resumes within the function where the parameters are popped off the stack to make them available to the code within the function.
This works great when function parameters are small pieces of datalike integers or floats or even smaller values. What if you have a very large object (we’ll cover creating classes and instantiating them in the next hour) that takes up hundreds—or even thousands—of bytes? Now the mere presence of this object on the stack is creating a headache for whatever runtime is executing your code. If you had to shovel things from one bucket to another bucket before you could do your work, would you rather shovel small fish tank pebbles or enormous boulders?
Enter pointers. Rather than placing all of the data belonging to the large object (the boulder) on the stack, we can instead write down the location of the boulder onto a small pebble and place that small pebble on the stack instead. This way, we can now quickly and easily pass all of our parameters to a function using the stack. When the function needs to access the large object/boulder, it can read the location from the pebble we put on the stack and go get the object on its own.
Obviously the Objective-C runtime doesn’t write down locations on pebbles. It does, however, store memory addresses in integers and place those on the stack.
Far more so than in other languages like C# or Java, you will be dealing with pointers on a daily basis in Objective-C. Just remembering that pointers are merely numbers on the stack that point to a different location on the heap will serve you well as you go through the rest of this book.
To see an example of pointers in action, follow these steps:
- Type the following code in after the last line of code you wrote for the previous section:
int targetNum = 42; int bigTargetNum = 75; int *targetNum_ptr = &targetNum; int *targetNum_ptr2 = &targetNum; NSLog(@"targetNum = %d, ptr points to = %d", targetNum, *targetNum_ptr); NSLog(@"are two pointers equal? %d", (targetNum_ptr == targetNum_ptr2)); targetNum_ptr2 = &bigTargetNum; NSLog(@"are two pointers equal 2nd time? %d", (targetNum_ptr == targetNum_ptr2)); *targetNum_ptr2 = 42; // what did this really do? NSLog(@"are two pointers equal 3rd time? %d", (targetNum_ptr == targetNum_ptr2)); NSString *stringA = @"Hello World"; NSString *stringB = [NSString stringWithFormat:@"%@ %@", @"Hello", @"World"]; NSLog(@"Is %@ == %@ : %d", stringA, stringB, (stringA == stringB)); NSLog(@"What about with isEqualToString? %d", [stringA isEqualToString:stringB]);
- Before running the application, try to predict what the output might be. On the first two lines, we create two standard integers. These are standard integers and we are storing their actual values.
On the next two lines we create two pointers. First, take note of the * operator. In this case, we are not multiplying. The presence of an asterisk before a variable name in a declaration statement indicates that we are declaring a pointer. This informs Objective-C that the data being stored in this variable is a memory location (pointer to real data), and not the actual data.
On these same lines of code, we use the address-of operator (&) to obtain the memory address of the number stored in the location called targetNum. At this point, both targetNum_ptr and targetNum_ptr2 point to the address of targetNum.
On the next line, we use the asterisk again, but this time as a dereferencing operator. This means that when Objective-C expects a value and you give it a pointer variable preceded by an asterisk, Objective-C will go fetch the value from the memory location currently stored within that pointer.
The next set of statements attempts to get a definitive answer to the question: Are two pointers equal if they both point to the same memory location? Running the application shows you that this is indeed true. For example, suppose the memory address holding the value for targetNum was 100080. If we set targetNum_ptr and targetNum_ptr2 to &targetNum, we set both of those variables to 100080, which makes them equivalent.
The next question is a little more tricky: If two pointers point to two different locations, but each location contains the same value, are those pointers equal?
This is the key to understanding pointers and equality. When you compare pointers, you are essentially asking if the address of the underlying data is the same. If you want to compare whether the data itself is the same, you need to either dereference the pointers and compare the underlying data or, better yet, use classes that know how to compare themselves.
This leads us to the NSString class. The first variable, stringA, is assigned to a string literal “Hello World”. Remember that the @ operator is actually a shortcut for creating an instance of an immutable string. If I had assigned stringB to @"Hello World", both stringA and stringB would be considered equivalent via the == operator because both would point to the single memory location on the heap where the immutable string “Hello World” resides.
Because we used two different methods to create “Hello World”, one constant and one via concatenation, neither stringA nor stringB points to the same memory location. However, because NSString knows how to do string comparison via the isEqualToString method (there is an abundance of other related string-comparison tools for more involved scenarios), you can compare the two strings and verify that the content is equivalent, even though both copies of “Hello World” are sitting in two difference places on the heap.
- When you run the application you’ve been working on this hour, the output related to pointers and string comparison should look like the following:
2011-05-29 13:31:04.788 Hour3[1325:903] targetNum = 42, ptr points to = 42 2011-05-29 13:31:04.789 Hour3[1325:903] are two pointers equal? 1 2011-05-29 13:31:04.790 Hour3[1325:903] are two pointers equal 2nd time? 0 2011-05-29 13:31:04.792 Hour3[1325:903] are two pointers equal 3rd time? 0 2011-05-29 13:31:04.793 Hour3[1325:903] Is Hello World == Hello World : 0 2011-05-29 13:31:04.794 Hour3[1325:903] What about with isEqualToString? 1
Finally, before moving on to the next section, it is worth noting that Objective-C strings are in fact Unicode strings. This means that these strings are not limited to storing text in English or similar 7- and 8-bit languages. Unicode strings can be used to store text in Chinese, Japanese, Hindi, Arabic, and any other language that uses text characters whose ordinal value has a value higher than 255 (often referred to as long characters or multibyte characters).