Using Objective-C Preprocessor Directives

From Techotopia
Revision as of 16:07, 12 November 2009 by Neil (Talk | contribs)

Jump to: navigation, search

In general terms the compilation of Objective-C programs from source code to executable binary is a three phase process. In the first phase, a tool called the preprocessor scans the human written source code and converts it to compiler friendly content and format. In the second phase the compiler generates object code (usually in the form of file with a .o filename extension) from the preprocessed source code. Finally, the linker brings all the object code modules and libraries together, resolves symbol references and creates the executable binary.

The preprocessor phase also searches for special directives written by the programmer and converts them to code that can be handled by the compiler. Each directive begins with a hash (#) character, and serves to make the Objective-C programming task easier and code easier to read and manage. In the remainder of this chapter of Objective-C 2.0 Essentials we will look at these preprocessor directives.


Contents


The #define Statement

The #define statement can be used for a variety of purposes and is probably the most flexible of preprocessor directives. Perhaps one of the most common uses is to give frequently used constant values in a program a programmer friendly and single point of definition. For the sake of an example, lets say you need to frequently use the boiling temperature of water (a point of contention but for the purposes of this example we will assume it to be 99.61 degrees Celsius) throughout your Objective-C program. One option might be to simply enter the constant value wherever it is needed. For example:

double liquidTemp = 99.61 - ambientTemp;

The above code works but doesn't do much to explain why the number 99.61 is used. Another problem with this approach is that if one the program needs to be modified to use a different temperature for the boiling point of water (like I said, this is a point of contention) you will have to change every instance of 99.61 in your code, while making sure you don't also change any instances of 99.61 that aren't related to the boiling point of water.

A much better approach is to assign the value a human friendly name using the #define directive:

#define BOILTEMP 99.61

Now, whenever the boiling point is needed in the code it can be referenced by the defined name instead of the constant value:

double liquidTemp = BOILTEMP - ambientTemp;

Now when we look at the code we can ascertain what is happening simply because the constant now has a meaningful name. In addition, if we ever need to specify a different temperature we just change the #define value and the change will be picked up by all references to the definition.

A #define directive can also contain an expression. A somewhat contrived example being:

#define TWOBYTWO 2 * 2

This can then be referenced in code. Continuing with our example, the following code will output "The result is 4":

NSLog (@"Result is %i", TWOBYTWO);

Creating Macros with the #define Statement

The #define statement may also be used in a more advanced fashion to create small fragments of code called macros. These can be thought of as small functions that can accept arguments, perform operations and return results. The following definition declares a macro designed to multiply two numbers together:

#define CalcInterest(x,y) ( x * y )

Having declared the macro, we can now call it from the code:

int earnings = CalcInterest(10,5));


Changing the Objective-C Language with #define

My first ever job involved writing communications software using the C programming language (on which Objective-C is based). I inherited some code written by a former employee who loathed the C language and preferred to use another programming language (the name of which escapes me). When I looked his C code it looked nothing like any C code I had ever seen before in my life. After about an hour of trying to understand how this could be possible (surely the compiler should have refused to compile this) I realized the other programmer had using the #define compiler directive to "modify" the syntax of the C programming language to make it look more like his preferred language. Whilst I am not suggesting that you too go to these lengths it is worth knowing that such adaptability is provided by the #define preprocessor statement.

Lets begin with a simple example and write a definition that assigns the word MINUS to the minus sign (-):

#define MINUS -

Having done this, we can now perform subtractions by using the the word MINUS:

int result;

result = 20 MINUS 10;

NSLog (@"Result = %i", result);

Now suppose that you, rather like the programmer whose code I inherited, dislike the curly braces ({}) used to encapsulate code blocks on Objective-C. You could, if you wish declare the words Begin and End to represent the open and close braces as follows:

#define Begin {
#define End }

Having done this, you would then be able to write code that looks like this (and still have it compile):

#import <Foundation/Foundation.h>

#define Begin {
#define End }

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

        int i;
        int j = 10;

        for (i=0; i<10; i++)
        Begin
                j += i;
                NSLog (@"j = %i", j);
        End

        [pool drain];

        return 0;
End

Undefining a Definition with #undef