Pointer Parameters and the Address Operator

Objective-C is chock-a-block with pointers (and asterisks). Objective-C methods typically expect pointer parameters and return a pointer value. But this doesn’t make things more complicated, because, as I’ve already mentioned, your variables referring to Objective-C objects are pointers. Pointers are what Objective-C expects, but pointers are also what Objective-C gives you. Pointers are exactly what you’ve got, so there’s no problem.

For example, one way to concatenate two NSStrings is to call the NSString method stringByAppendingString:, which the documentation tells you is declared as follows:

- (NSString *)stringByAppendingString:(NSString *)aString

This declaration is telling you (after you allow for the Objective-C syntax) that this method expects one NSString* parameter and returns an NSString*. That sounds messy, but it isn’t, because every NSString is really an NSString*. So nothing could be simpler than to obtain a new NSString consisting of two concatenated NSStrings:

NSString* s1 = @"Hello, ";
NSString* s2 = @"World!"
NSString* s3 = [s1 stringByAppendingString: s2];

Sometimes, however, a function or method expects as a parameter a pointer to a thing, but what you’ve got is not that pointer but the thing itself. Thus, you need a way to create a pointer to that thing. The solution is the address operator (K&R 5.1), which is an ampersand before the name of the thing.

For example, there’s an NSString method for reading from a file into an NSString, which is declared like this:

+ (id)stringWithContentsOfFile:(NSString *)path
                      encoding:(NSStringEncoding)enc
                         error:(NSError **)error

Now, never mind what an id is, and don’t worry about the Objective-C method declaration syntax. Just consider the types of the parameters. The first one is an NSString*; that’s no problem, as every reference to an NSString is actually a pointer to an NSString. An NSStringEncoding turns out to be merely an alias to a primitive data type, an NSUInteger, so that’s no problem either. But what on earth is an NSError**?

By all logic, it looks like an NSError** should be a pointer to a pointer to an NSError. And that’s exactly what it is. This method is asking to be passed a pointer to a pointer to an NSError. Well, it’s easy to declare a pointer to an NSError:

NSError* myError;

But how can we obtain a pointer to that? With the address operator! So our code might look, schematically, like this:

NSString* myPath = // something or other;
NSStringEncoding myEnc = // something or other;
NSError* myError = nil;
NSString* result = [NSString stringWithContentsOfFile: myPath
                                             encoding: myEnc
                                                error: &myError];

The important thing to notice is the ampersand. Because myError is a pointer to an NSError, &myError is a pointer to a pointer to an NSError, which is just what we’re expected to provide. Thus, everything goes swimmingly.

This method effectively returns two results. It returns a real result, which we have captured by assigning it to the NSString pointer we’re calling result. But if there’s an error, it also wants to set the value of another object, an NSError object; the idea is that you can then study that NSError object to find out what went wrong. (Perhaps the file wasn’t where you said it was, or it wasn’t stored in the encoding you claimed it was.) By passing a pointer to a pointer to an NSError, you give the method free rein to do that. Before the call to stringWithContentsOfFile:, myError was initialized to nil; during the call to stringWithContentsOfFile:, if there’s an error, the pointer is repointed, thus giving myError a meaningful NSError value describing that error. (Repointing a pointer in this way is sometimes called indirection.)

So the idea is that you first check result to see whether it’s nil. If it isn’t, fine; it’s the string you asked for. If it is, you then study the NSError that myError is now pointing to, to learn what went wrong. This pattern is frequently used in Cocoa.

You can use the address operator to create a pointer to any named variable. A C function is technically a kind of named variable, so you can even create a pointer to a function! This is an example of when you’d use the name of the function without the parentheses: you aren’t calling the function, you’re talking about it. For example, &square is a pointer to the square function. Moreover, just as the bare name of an array is implicitly a pointer to its first element, the bare name of a function is implicitly a pointer to the function; the address operator is optional. In Chapter 3, I describe a situation in which specifying a pointer to a function is a useful thing to do.

Another operator used in connection with pointers, or when memory must be allocated dynamically, is sizeof. It may be followed by a type name in parentheses or by a variable name; a variable name needn’t be in parentheses, but it can be, so most programmers ignore the distinction and use parentheses routinely, as if sizeof were a function.

For example, the documentation shows the declaration for the CTParagraphStyleSetting struct like this:

typedef struct CTParagraphStyleSetting {
    CTParagraphStyleSpecifier spec;
    size_t valueSize;
    const void* value;
} CTParagraphStyleSetting;

A CTParagraphStyleSpecifier is just an enum, so concentrate on the second and third elements of this struct, valueSize and value. The value element is a pointer-to-void, meaning an anonymous pointer; it’s a pointer to something-or-other. Since pointer-to-void casts away any concern for what data type the pointer is pointing to, it also casts away any knowledge of how big that data type is. The job of the second element of the struct is to provide that information. Here’s an actual example of initializing a CTParagraphStyleSetting struct (from Chapter 23):

CTTextAlignment centerValue = kCTCenterTextAlignment;
CTParagraphStyleSetting center =
    {kCTParagraphStyleSpecifierAlignment, sizeof(centerValue), &centerValue};

Get Programming iOS 6, 3rd Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.