Программирование на Objective-C 2.0
Шрифт:
Одной из наиболее необычных конструкций в языке программирования Objective-C является объединение (union). Эта конструкция используется в ос-новном для сложных программных приложений, когда требуется сохранять раз-личные типы данных в одном и том же месте памяти. Предположим, что нужно определить одну переменную с именем х, которую можно было бы использовать для хранения одного символа, числа с плавающей точкой или целого числа. Для этого можно определить объединение, например, с именем mixed. union mixed { char с; float f; int i; };
Объявление объединения совпадает со структурой (за исключением ключевого слова union). Принципиальным отличием между структурами и объединениями
не означает, что х содержит три отдельных компонента с именами с, f или i. На самом деле х содержит один компонент, который называется с, f или L Тем самым мы можем использовать переменную х для хранения элемента типа char, float или int, но не всех трех типов одновременно. Чтобы сохранить какой-либо символ в переменной х, используется оператор х.с = 'К';
Чтобы сохранить в х значение с плавающей точкой, используется форма за-писи x.f: x.f = 786.3869;
И, наконец, чтобы сохранить в х результат деления целой переменной count на 2, используется оператор x.i = count / 2;
Поскольку элементы х типа float, char и int находятся в одном месте памяти, мы можем единовременно сохранять в х только одно из этих значений. Кроме того, значение, которое считывается из объединения, должно соответствовать тому, что было в последний раз записано в это объединение.
При определении объединения имя объединения указывать не обязательно, и переменные можно объявлять одновременно с определением этого объединения. В объединениях можно объявлять указатели с таким же синтаксисом и правилами выполнения операций, как для структур. Мы можем инициализировать переменную типа union следующим образом. union mixed х = {'#'};
Первому члену х, то есть с, присваивается символ #. Определенный член объединения можно также инициализировать по имени следующим образом. union mixed х = {.f= 123.4;};
Мы можем инициализировать автоматическую union-переменную типа присвоив ей другую union-переменную того же типа.
С помощью объединения можно определять массивы для хранения элементов данных различного типа. Ниже задается массиве именем table, содержащий kTableEntries элементов. struct { char *name; int type; union { int i; float f; char c; } data; } table [kTableEntries];
Каждый элемент этого массива содержит структуру, включающую указатель на типа char с именем паше, целый компонент с именем type и union-компонент с именем data. Каждый элемент data может содержать компонент типа int, float или char. Целый компонент type позволяет следить за типом значения, сохраняемого в data. Мы можем присвоить type значение INTEGER (определенное соответствую-щим образом), если содержится значение типа int; значение FLOATING, если со-держится значение типа float; CHARACTER, если содержится значение типа char. Эта информация позволяет узнать, как обращаться к определенному компоненту структуры data определенного элемента массива.
Чтобы сохранить символ '#' в элементе table[5] и затем записать в поле type, что в этом месте хранится символ, применяются два оператора table[5].data.c = '#'; table[5].type = CHARACTER;
Во время перебора элементов table можно определять тип значения данных, хранящегося в каждом элементе, с помощью набора проверок. Например, в следующем цикле выводится имя и его значение из массива table. enum symbolType {INTEGER, FLOATING, CHARACTER }; ... for (j = 0; j < kTableEntries; ++j) { NSLog (@"%s", table[j].name); switch (table [j], type ) { case INTEGER: NSLog (@"%i", tableOl-data.i); break; case FLOATING: NSLog (@"%g", table[j].data.f); break; case CHARACTER: NSLog (@"%cn, table[j].data.c); break; default: NSLog (@"Unknown type (%i), element %i", table(j]-type, j); break; } }
На
практике такое приложение можно использовать для хранения таблицы символов, содержащей имя каждого символа, его тип и его значение (и другую информацию). 13.6. Это не объекты!Теперь вы знаете, как определять массивы, структуры, символьные строки и объединения и как работать с ними в программах. Помните главное: они не яв-ляются объектами. Мы не можем передавать им сообщения. Они не позволяют полностью использовать такие удобные возможности, как стратегию выделения памяти, которая обеспечивается в Foundation framework. Это одна из причин, по которым я советовал пропустить эту главу и вернуться к ней позже. Вы лучше подготовлены к изучению того, как использовать классы Foundation, в которых массивы и строки определяются как объекты, чем к использованию таких средств, встроенных в язык. Прибегайте к использованию типов, описанных в этой главе, только в случае реальной необходимости! 13.7. Различные средства языка
Некоторые средства языка не совсем согласуются с материалом других глав, поэтому мы включили их в этот раздел. Составные литералы
Составной литерал (compound literal) представляет собой имя типа, заключенное в круглые скобки, после которого следует список инициализации. В результате создается неименованное значение указанного типа, область действия которого ограничена блоком, в котором оно создано, или глобальной областью действия, если оно определено вне блока. В последнем случае все инициализаторы должны включать только константные выражения.
Рассмотрим следующий пример. (struct date) {.month = 7, .day = 2, .year = 2004}
Это выражение создает структуру типа struct date с указанными начальными значениями. Ее можно присвоить другой структуре типа struct date, например, theDate = (struct date} {.month = 7, .day = 2, .year = 2004};
Ее можно передать функции или методу как аргумент типа struct date, напри-мер, setStartDate ((struct date) {.month = 7, .day = 2, .year = 2004});
Кроме того, можно определять типы, отличные от структур. Например, если intPtr имеет тип int *, оператор intPtr = (int (100]) {(0) = 1, [50] = 50, [99] = 99 };
который может появиться в любом месте программы, задает intptr, указывающий на массив из 100 целых элементов, первые 3 элемента которого инициализируются указанным образом.
Если размер массива не задан, он определяется списком инициализации. Оператор goto
Оператор goto вызывает непосредственный переход в указанную точку програм-мы. Чтобы указать это место, требуется метка. Метка (label) — это имя, форми-руемое по таким же правилам, как имена переменных; сразу после него ставится двоеточие. Метка ставится непосредственно перед оператором, на который выполняется переход, и должна присутствовать в той же функции или методе, где находится соответствующий goto.
Например, оператор goto out_of_data;
вызывает переход к оператору, перед которым стоит метка out_of_data;. Эта метка должна находиться где-либо в функции или методе (до или после goto) и может использоваться, например, как показано ниже. out_of_data: NSLog (@"Unexpected end of data.");
Ленивые профаммисты часто злоупотребляют оператором goto для перехода к другим частям своего кода. Оператор goto нарушает нормальную последователь-ность программы, что затрудняет отслеживание ее выполнения. В практике на-дежною профаммирования не рекомендуется использовать операторы goto. Пустой оператор