Программирование на Objective-C 2.0
Шрифт:
что даст синтаксическую ошибку. Ставить точку с запятой после операторов define можно только в тех случаях, когда это действительно нужно.
Определение для препроцессора может не быть допустимым выражением Objective-C, но результирующее выражение в программе должно быть допусти мым. Например, вы можете задать определения: #define AND && #define OR ||
А затем писать такие выражения, как if ( х > 0 AND х < 10)
и if ( у == 0 OR у == value )
Можно даже использовать «define для оператора проверки равенства: #define EQUALS ==
И затем написать выражение if (у EQUALS 0 OR
Это позволяет избежать ошибки в тех случаях, когда для проверки равенства используют один знак равенства.
Возможности оператора «define велики, но практика надежного программирования не приветствует такие способы переопределения синтаксиса базового языка. Кроме того, это затрудняет другим людям понимание вашего кода.
Это еще не все. Поскольку определенное значение может ссылаться на другое определенное значение, следующие две строки «define допустимы. #define PI 3.141592654 #define TWO_PI 2.0 * PI
Имя TWO_PI определяется со ссылкой на предыдущее определенное ими PI, что избавляет от необходимости повторно писать значение 3.141592654.
Обратный порядок этих определений тоже допустим. #define TWO PI 2.0 * PI #define PI 3.141592654
Определенные значения можно использовать в определениях, если все, что нужно, определено на тот момент, когда соответствующее определенное имя используется в программе.
Разумное использование операторов #define позволяет сократить количество комментариев в программе. Рассмотрим оператор if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
В выражении проверяется, високосный ли год записан в переменной year.
Рассмотрим оператор #define и оператор if. #define IS_LEAP_YEAR year % 4 == 0 && year % 100 != 0 \ ||year % 400 == 0 if (IS_LEAP_YEAR)
Обычно препроцессор предполагает, что определение целиком содержится в одной строке. Символом переноса строки (т.е. продолжения оператора) для препроцессора является обратный слэш. Этот символ игнорируется при подстановке. Если строк продолжения несколько, то каждая строка должна заканчиваться обратным слэшем.
Последний оператор if намного понятнее, чем предыдущий. Конечно, данное определение позволяет проверить на високосный год только переменную year, но мы можем написать определение, с помощью которого можно проверить на високосный год любое выражение. Для этого нужно включить в определение один или несколько аргументов.
IS_LEAP_YEAR можно определить с аргументом у следующим образом. #define IS_LEAP_YEAR(y) у % 4 == 0 && у % 100 != 0 \ || у % 400 == 0
В отличие от определения метода, мы не определяем тип аргумента у, поскольку здесь выполняется просто подстановка текста, а не вызов функции. Отметим, что в определении имени с использованием аргументов не допускаются пробелы между определенным именем и левой круглой скобкой перед списком аргументов.
Используя это определение, напишем оператор if ( IS_LEAP_YEAR (year))
Оператор проверяет, является ли значение для year високосным годом. Можно написать аналогичное выражение для nextYear. if ( IS_LEAP YEAR (nextYear))
В этом операторе определение для IS_LEAP_YEAR непосредственно подставляется в
оператор if с заменой аргумента у на nextYear. В результате компилятор будет обрабатывать следующий оператор. if ( nextYear % 4 == 0 && nextYear % 100 != О |j nextYear % 400 == 0)Определения часто с одним или несколькими аргументами то называют макросами. В следующем макросе представлен квадрат его аргумента. #define SQUARE(x) х * х
В определении для SQUARE необходимо учитывать возможность следующей ошибки. Исходя из нашего описания, оператор у = SQUARE (v);
присваивает у значение V2. Но в операторе у = SQUARE (v+1);
переменной не присваивается значение (v + 1)2, как ожидалось. Поскольку в определении этого макроса препроцессор выполняет подстановку текста вместо аргумента, здесь получится следующее выражение: у = v + 1 * v + 1;
Чтобы решить проблему, нужно использовать в определении макроса SQUARE круглые скобки. #define SQUARE(x) ( (х) * (х))
Определение может показаться несколько странным, но помните, что х будет заменяться подставляемым выражением. Теперь оператор у = SQUARE (v+ 1);
будет правильно обработан как У = ((v+ 1) * (v+ 1)); Следующий макрос позволяет создавать новые дроби из нашего класса Fraction. #define MakeFract(x.y) ([[Fraction alloc] initWith: x over: y]])
Теперь для сложения дробей nl/dl и n2/d2 можно писать такие выражения, как myFract = MakeFract (1, 3); // Создание дроби 1/3
или sum = [MakeFract (nl, d1) add: MakeFract (n2, d2)];
Макросы удобны для работы с условными выражениями. В следующей строке определяется макрос с именем МАХ, определяющий максимальное из двух значений. #define MAX(a,b) { ((а) > (Ь)) ? (а): (Ь))
С помощью этого макроса можно писать такие операторы, как limit = МАХ (х + у, minValue);
Переменной limit присваивается максимальное из двух значений: х + у и minValue. Все определение МАХ заключено в круглые скобки, чтобы правильно обрабатывать такие выражения, как МАХ (х, у)* 100
Каждый аргумент тоже заключен в круглые скобки, чтобы правильно обрабатывать такие выражения, как МАХ (х & у, z)
Оператор & — это побитовый оператор AND, и он имеет меньший приоритет, чем оператор > в данном макросе. Без этих круглых скобок оператор > обрабатывался бы раньше побитового AND, что давало бы неверный результат.
В следующем макросе проверяется, является ли символ строчной буквой. #define IS_LOWER_CASE(x) ( ((х) >= 'а') && <(х) <= ’z') )
С помощью этого макроса можно писать такие выражения, как if ( IS_LOWER_CASE (с))
Этот макрос можно даже использовать в определении другого макроса, чтобы преобразовывать символ из нижнего регистра в верхний (делать строчную букву прописной), не изменяя другие символы. #define T0_UPPER(x) ( IS_LOWER_CASE (х) ? (х) -'a' + 'A' : (х))
Здесь мы снова работаем со стандартным набором символов ASCII. В части II, когда будут описываться объекты-строки, вы увидите, как выполнять преобразование символов из одного регистра в другой для международных наборов символов (Unicode). Оператор