Программирование на Objective-C 2.0
Шрифт:
Перечислимый (enumerated) тип данных позволяет определять имя для типа данных, которое будет использоваться только для хранения заданного списка значений. В языке Objective-C оператор typedef позволяет вам назначать собственное имя встроенному или производному типу данных. В этой главе мы описываем действия компилятора по преобразованию типов данных при оценке выражений. 10.1. Инициализация классов
Мы уже встречали такой набор действий, когда выделяется память для нового экземпляра объекта, а затем выполняется его инициализация: Fraction *myFract = [[Fraction alloc] init];
После вызова этих методов обычно
Процесс инициализации объекта, после которого ему присваиваются начальные значения, часто объединяют в один метод. Например, можно определить метод initWith::, который инициализирует объект типа fraction (дробь) и присваивает два (неименованных) заданных аргумента его числителю (numerator) и знаменателю (denominator).
Класс, который содержит много методов и переменных экземпляра, обычно имеет несколько методов инициализации. Например, класс NSArray из Foundation framework содержит шесть методов инициализации. initWithArray: initWithArrayxopyltems: initWithContentsOfFile: initWithContentsOfURL: initWithObjects: initWithObjects:count:
Массиву (array) можно выделить память и затем инициализировать его, например, с помощью следующей последовательности: myArray = [[NSArray alloc] initWithArray: myOtherArray];
Принято, что все инициализаторы в классе обычно начинаются с init. Инициализаторы NSArray следуют этому правилу. При написании инициализаторов вы можете придерживаться одной из двух стратегий.
Если ваш класс содержит более одного инициализатора, один из них должен быть вашим назначенным (designated ) инициализатором, и все остальные методы инициализации должны его использовать. Обычно это более сложный метод инициализации (и принимает больше всего параметров). При создании назначенного инициализатора основной код инициализации объединяется в одном методе. При создании подкласса можно затем замещать назначенный инициализатор, чтобы обеспечить правильную инициализацию новых экземпляров. Необходимо следить за тем, чтобы правильно инициализировались любые наследуемые переменные экземпляра. Наиболее простой способ —вызывать сначала назначенный метод инициализации из родительского класса, который обычно называется init, а после этого инициализировать свои собственные переменные экземпляра.
Исходя из этого, метод инициализации initWith:: для класса Fraction может выглядеть следующим образом. -(Fraction *) initWith: (int) n: (int) d { self = [super init]; if (self) [self setTo: n over: d]; return self; }
Этот метод вызывает сначала родительский инициализатор, которым является метод init из NSObject (напомним, что это родительский класс для Fraction).
Вы должны присвоить результат self, поскольку инициализатор имеет право изменять или перемещать объект в памяти.
После инициализации super (и ее успешного завершения, что указывается ненулевым возвращаемым значением) используется метод setTo:over:, чтобы задать числитель (numerator) и знаменатель (denominator) дроби (Fraction). Как и для других методов инициализации, предполагается, что вы возвращаете инициализированный объект.
В программе 10.1 выполняется проверка нового метода инициализации initWith::. Программа 10.1 #import "Fraction.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *a, *b; a = [[Fraction alloc] initWith: 1: 3]; b = [[Fraction alloc] initWith: 3: 7]; [a print]; [b print]; [a release]; [b release]; [pool drain]; return 0; }
Вывод
Когда программа начинает выполнение, она передает метод вызова инициализации всем нашим классам. Если имеется класс и связанный с ним подкласс, родительский класс получает это сообщение первым. Это сообщение передается каждому классу только один раз, и оно гарантированно отправляется до того, как любые другие сообщения будут переданы классу. Ваша цель в этот момент — выполнение инициализации любого класса. Например, вам может потребоваться инициализация некоторых статических переменных, связанных с данным классом. 10.2. Снова об области действия
На область действия переменных в программе можно влиять разными способами: с переменными экземпляра или с обычными переменными, определенными вне или внутри функций. Ниже мы будем использовать термин модуль (module ) при ссылке на любое число определений методов или функций, содержащихся в одном исходном файле. Директивы для управления областью действия переменных экземпляра
Вы уже знаете, что переменные экземпляра имеют область действия, которая ограничивается методами экземпляра, определенными для данного класса. Поэтому любой метод экземпляра может выполнять доступ к свои переменным экземпляра по имени без дополнительных действий. Вы также знаете, что переменные экземпляра наследуются подклассом. Д оступ к переменным экземпляра тоже можно выполнять по имени из любого метода, определенного в этом подклассе. И в этом случае специальные действия тоже не требуются.
Перед переменными экземпляра при объявлении в секции interface можно помещать четыре директивы, чтобы более точно управлять их областью действия.
@protected. Методы, определенные в данном классе и любых подклассах, могут выполнять непосредственный доступ к последующим переменным экземпляра. Это вариант по умолчанию.
@private. Методы, определенные в данном классе (но не в подклассах), могут выполнять непосредственный доступ к последующим переменным экземпляра.
@public. Методы, определенные в данном классе и любых классах или модулях, могут выполнять непосредственный доступ к последующим переменным экземпляра.
@package. Для 64-битных образов доступ к переменной экземпляра может выполняться в любом месте образа, который реализует данный класс.
Если вам нужно определить класс с именем Printer, содержащий две частные переменные экземпляра с именами pageCount и tonerLevel, которые доступны только методам из класса Printer, то вы можете использовать следующую секцию interface. @interface Printer: NSObject { @ private int pageCount; int tonerLevel; @ protected // другие переменные экземпляра } @end
Доступ к этим двум переменным экземпляра нельзя выполнить из любого подкласса класса Printer, поскольку они сделаны частными (private).
Эти специальные директивы действуют как переключатели: все переменные, которые появляются после одной из этих директив (пока не появится правая фигурная скобка, которая являются концом объявлений этих переменных), имеют указанную область действия, если не использована другая директива. В приведенном примере директива @protected гарантирует, что следующие после нее переменные экземпляра будут доступны для методов подклассов и класса Printer.