Программирование на Objective-C 2.0
Шрифт:
Если следующий оператор помещен вне любого метода (или функции), то значение gGlobalVar будет доступно из любой последующей точки файла, содержащей это определение, но будет недоступно из методов или функций, содержащихся в других файлах. static int gGlobalVar = 0;
Напомним, что методы класса не имеют доступа к переменным экземпляра (подумайте, почему это относится к данному случаю). Но вам может потребоваться, чтобы какой-либо метод класса имел доступ к переменным и мог задавать их значения. В качестве простого примера можно указать метод класса, выделяющий память для объектов (alloc), который должен следить за числом объектов. Это можно сделать, создав статическую переменную внутри файла секции implementation
В программе 10.2 определение класса Fraction расширяется за счет добавления двух новых методов. Метод класса allocF выделяется память для нового объекта типа Fraction и следит за числом дробей (объектов Fraction), которые он выделил, а метод count возвращает значение этого счетчика. Метод count тоже является методом класса. Его можно было бы реализовать как метод экземпляра, но лучше запросить класс, сколько экземпляров он выделил, вместо передачи сообщения определенному экземпляру этого класса.
Ниже приводятся объявления для двух новых методов класса, добавленные в файл Fraction.h. +(Fraction *) allocF; +(int) count;
Отметим, что здесь не замещается наследуемый метод alloc; вместо этого определяется наш собственный метод выделения памяти. В этом методе будет использоваться наследуемый метод alloc. Следующий код нужно поместить в файл секции implementation Fraction.m. static int gCounter; @implementation Fraction +(Fraction *) allocF { extern int gCounter; ++gCounter; return [Fraction alloc]; } +(int) count { extern int gCounter; return gCounter; } // здесь находятся другие методы из класса Fraction @end
Примечание. В практике надежного программирования не принято замещать метод alloc, поскольку он работает с физическим местоположением в памяти. Не следует вмешиваться в работу системы на этом уровне.
Объявление static для переменной gCounter делает ее доступной для любого метода, определенного в секции implementation, но при этом она недоступна вне этого файла. Метод allocF просто наращивает значение переменной gCounter и затем использует метод alloc для создания новой дроби (Fraction), возвращая результат. Метод count просто возвращает значение счетчика (gCounter), не давая пользователю непосредственный доступ к этой переменной.
Напомним, что объявления extern не требуются в этих методах, поскольку переменная gCounter определена внутри этого файла. Это просто помогает читателю метода понять, что выполняется доступ к переменной, определенной вне метода. Префикс g в имени переменной предназначен для той же цели, поэтому большинство программистов обычно не включают объявления extern. В программе Ю.2 выполняется тестирование этих методов. #import "Fraction.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *a, *b, *c; NSLog (@"Fractions allocated: %i", [Fraction count]); a = [[Fraction allocF] init]; b = [[Fraction allocF] init]; c = [[Fraction allocF] init]; NSLog (@"Fractions allocated: %i", [Fraction count]); [a release]; [b release]; [c release]; [pool drain]; return 0; }
Вывод программы 10.2 Fractions allocated: 0 (Выделено объектов Fraction) Fractions allocated: 3
Когда начинается выполнение этой программы, значение gCounter автоматически задается равным 0 (напомним, что вы
можете замещать наследуемый метод инициализации класса, если хотите выполнить специальную инициализацию класса в целом, например, присвоить статическим переменным некоторые ненулевые значения). После выделения (и последующей инициализации) трех объектов типа Fraction с помощью метода allocF метод count считывает переменную counter, значение которой действительно стало равным 3. Вы можете добавить метод-установщик (setter) для этого класса, если хотите выполнять сброс счетчика или задавать для него определенное значение, но в данном случае это не требуется. 10.3. Описатели хранения для классаВы уже познакомились с описателями хранения для класса, которые можно помещать перед именами переменных. Здесь мы рассмотрим другие описатели, которые предоставляют компилятору информацию о предполагаемом использовании переменной в программе. auto
Это ключевое слово используется для объявления автоматической локальной переменной (в противоположность статической). Это описатель по умолчанию для объявления переменной внутри функции или метода, но никто не использует его в явном виде. auto int index;
Это объявление переменной index как автоматической локальной переменной; это означает, что для нее выполняется автоматическое выделение памяти при входе в блок (которым может быть заключенная в фигурные скобки последовательность операторов, метод или функция) и автоматическое освобождение при выходе из этого блока. Поскольку это происходит по умолчанию внутри блока, оператор int index;
эквивалентен оператору auto int index;
В отличие от статических переменных, которые имеют по умолчанию начальные значения 0, автоматические переменные остаются неопределенными, пока вы не присвоите им значения в явном виде. const
Компилятор позволяет связывать атрибут const с переменными, значения которых не будут изменяться. Иначе говоря, он сообщает компилятору, что указанные переменные имеют постоянное значение при выполнении программы. Если попытаться присвоить значение переменной, объявленной с атрибутом const, после инициализации, или попытаться выполнить ее наращивание или уменьшение, компилятор выдаст предупреждающее сообщение. Ниже в переменной pi используется атрибут const: const double pi = 3.141592654;
Он указывает компилятору, что программа не будет изменять эту переменную. Естественно, значение такой переменной должно быть инициализировано при ее определении.
Определение переменной с атрибутом const указывает читателю, что программа не будет изменять значение этой переменной. volatile
Это описатель, противоположный const. Он в явном виде указывает компилятору, что соответствующая переменная будет изменять свое значение. Он включен в язык Objective-C, чтобы компилятор не оптимизировал операторы, которые кажутся избыточными, и выполнял повторную проверку переменной, когда ее значение, казалось бы, не изменяется. Типичный пример — порт ввода-вывода, (I/O) (подробнее см. главу 13).
Предположим, что у вас имеется адрес выходного порта, хранящегося в программе в переменной с именем outPort. Записать в этот порт два символа, например 0 и N, можно с помощью следующего кода. *outPort = 'O’; *outPort = ’N’;
В первой строке указывается, что символ 0 нужно сохранить по адресу памяти, указанному переменной outPort. Во второй строке указывается, что символ N нужно сохранить по тому же адресу. Оптимизирующий компилятор может заметить, что это две последовательные записи по одному адресу, и поскольку outPort между ними не изменяется, может просто удалить первый оператор из программы. Чтобы этого не произошло, нужно объявить переменную outPort с атрибутом volatile, например: volatile char *outPort; 10.4. Перечислимые типы данных