Learn Objective-C: The Preprocessor
The preprocessor is a very powerful C-language feature that allows you to customize your code to make it more portable and/or readable. Using the processor, you can redefine (portions) of the Objective-C language to suit your personal style or the style of another language. The preprocessor runs before the compiler, and performs certain statements to the code. These statements are denoted by the pound sign (#
), which must be the first non-space character on that line.
The #define
Statement
The #define
statement is primarily used to assign meaningful names to constants. For example, if you were building a calendar program, you might have the line
#define MONTHS_PER_YEAR 12
Note the syntax. The #define
statement is followed by the name of the constant, then the value that it replaces with just a space in between, and no semicolon or anything else at the end. The naming convention for preprocessor definitions is for it to be in all caps (to identify a symbolic name), and with underscores between words. You may also see the name being written with a “k” in front of an otherwise ordinary variable name, such as in kMonthsPerYear. The reason is that the preprocessor sees the first group of symbols, up to a whitespace character (space, tab, etc) as the name of the constant, then when it hits the whitespace, whatever comes after is the substitution value. The preprocessor literally substitutes whatever the constant is for the value (basically like a search and replace), which is why you cannot place a semicolon at the end—otherwise, the semicolon would get substituted in as well. If this is desired, then a semicolon may be used, but in most cases, as above, it should not.
A defined name is not a variable; it’s value cannot be changed after it has been defined. #define
statements are usually located at the top of a file, after the #import
directives, but it is not strictly necessary. However, a name must be defined before it is used. Also important to note is that once a name has been defined, it can be used anywhere else in the program, not just in that file.
An additional example would be using #define to store certain values, such as in
#define PI 3.141592654
// areaOfCircle method could return "PI * radius * radius", which is a lot clearer than writing 3.141592654 everywhere you need the value.
Advanced Definitions
Note, however, that a preprocessor performs what is basically a search-and-replace through the code. This means that you can get many more complex definitions. We start with TWO_PI, the product of 2 and PI:
#define TWO_PI 2.0 * 3.141592645
But we can take this a step further. A defined value can reference another, previously defined value.
#define TWO_PI 2.0 * PI
Given a line of code such as TWO_PI * r, the compiler actually sees the following:
// TWO_PI * r
// 2.0 * PI * r
2.0 * 3.141592654 * r
The preprocessor substitutes in the values, and then the code is compiled.
You can also use the preprocessor to define any chunk of Objective-C, not necessary a valid expression. For instance, you could write
#define AND &&
#define OR ||
#define EQUALS ==
if (y EQUALS 0 OR y EQUALS input) // …
However, note that it is often considered bad practice to redefine portions of the language just for the sake of doing so, as it makes the code harder to understand.
Properly used #define
s, however, make the code more self documenting. For example, compare the following lines:
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
// OR
#define IS_LEAP_YEAR year % 4 == 0 && year % 100 != 0 || year % 400 == 0
if (IS_LEAP_YEAR) // …
A definition should be placed on a single line of code, but if multiple lines are needed, the end of each line should be a backslash character. Don’t worry about line wrapping though- if your IDE supports line wrapping (Xcode does), then it should not present an issue to the compiler if the line is wrapped by the software. This only applies if you put a line break in the line manually.
The preceding example is great, but you’re limited to using a variable called year. To give yourself more flexibility, you can write a definition that takes one or more arguments:
#define IS_LEAP_YEAR(y) y % 4 == 0 && y % 100 != 0 \
|| y % 400 == 0
if ( IS_LEAP_YEAR(currentYear) ) // …
Here, you’re not quite making a literal substitution- the argument passed in is first substituted into the #define, then the whole thing is substituted into your program.
Macros
A definition that takes arguments, particularly more than one, is often known as a macro:
#define SQUARE(x) x * x
Incidentally, the previous definition leads to an interesting problem. What would happen with this line:
y = SQUARE(v + 1);
Because of the literal substitution, what actually gets compiled is y = v + 1 * v + 1;
, which algebraically simplifies to y = 2 * v + 1;
. Therefore, the macro should be properly define as
#define SQUARE(x) ( (x) * (x) )
Therefore, y = SQUARE(v + 1);
becomes y = ( (v + 1) * (v + 1) );
.
The rule of thumb is to enclose each argument within the definition in parentheses, which should avoid the issue above.
The #
Operator
The #
preprocessor operator creates a C-style string out of the arguments.
#define string(x) #x
// string(testing) == "testing"
This is similar to Java’s String and System.out methods, which allow you to print any value without having to explicitly convert it into a string first. A practical example would be:
#define printint(var) printf (#var " = %i\n", var)
printint(count);
The previous line is expanded into printf(” count” ” = %i\n”, count);
, which, after concatenating two adjacent strings, results in printf(“count = %i\n”, count);
.
The ##
Operator
The ##
operator is used to join two tokens. The preprocessor takes the argument supplied to the macro and joins it with the token that follows or precedes the ##
.
Suppose you have a list of variables, named x1
through x100
. If you wanted to print a specific x-variable, you could write a macro like this:
#define printxvar(n) printf("%i\n", x ## n)
Therefore, printxvar(20);
is expanded to printf(“%i\n”, x20);
. You can make it a bit fancier:
#define printxvar(n) printint(x ## n)
Therefore, printxvar(10);
expands into printint(x10);
, then printf(“x10″ ” = %i\n”, x10);
, and finally printf(“x10 = %i\n”, x10);
.
This post is part of the Learn Objective-C in 24 Days course.
Previous Lesson | Next Lesson |