Программирование на Objective-C 2.0
Шрифт:
Вспомните, что говорилось в этой главе о типах ар1ументов и возвращаемых значений, и модифицируйте методы add: в классах Fraction и Complex, чтобы принимать и возвращать объекты типа id. Затем напишите программу, которая включает следующую последовательность кода. result = [dataValuel add: dataValue2]; [result print];
Здесь result, dataValuel и dataValue2 — это объекты типа id. He забудьте задать образом значения dataValuel и dataValue2 в программе и освободить (release) все объекты, прежде чем завершить программу.
Примечание. Вам придется изменить имна этих методов. Системный класс NSObjectController тоже содержит метод add:. Как говорилось выше в разделе «Типы аргументов и возвращаемых значений при динамическом контроле типов»,
Используя определения классов Fraction и Complex, заданные в этой книге, и определения Fraction *fraction = [[Fraction alloc] init]; Complex *complex = [[Complex alloc] init]; id number = [[Complex alloc] init]; определите возвращаемое значение для следующих выражений с сообщениями. Затем введите их в программу, чтобы проверить результаты. [fraction isMemberOfClass: [Complex class]]; [complex isMemberOfClass: [NSObject class]]; [complex isKindOfClass: [NSObject class]]; [fraction isKindOfClass: [Fraction class]]; [fraction respondsToSelector: @selector (print)]; [complex respondsToSelector: @selector (print)]; [Fraction instancesRespondToSelector: @selector (print)]; [number respondsToSelector: @selector (print)]; [number isKindOfClass: [Complex class]]; [number respondsToSelector: @selector (release)]; [[number class] respondsToSelector: @selector (alloc)];
Глава 10. Более подробно о переменных и типах данных
В этой главе мы поговорим об области действия переменных, методах инициализации для объектов и типах данных. В главе 7 мы кратко обсуждали область действия переменных экземпляра, статические и локальные переменные. Теперь мы более подробно поговорим о статических переменных и введем понятие глобальных и внешних переменных. Для компилятора Objective-C можно задавать директивы, позволяющие контролировать область действия переменных экземпляра. В этой главе мы рассмотрим их.
Перечислимый (enumerated) тип данных позволяет определять имя для типа данных, которое будет использоваться только для хранения заданного списка значений. В языке Objective-C оператор typedef позволяет вам назначать собственное имя встроенному или производному типу данных. В этой главе мы описываем действия компилятора по преобразованию типов данных при оценке выражений. 10.1. Инициализация классов
Мы уже встречали такой набор действий, когда выделяется память для нового экземпляра объекта, а затем выполняется его инициализация: Fraction *myFract = [[Fraction alloc] init];
После вызова этих методов обычно выполняется присваивание некоторых значений новому объекту: [myFract setTo: 1 over: 3];
Процесс инициализации объекта, после которого ему присваиваются начальные значения, часто объединяют в один метод. Например, можно определить метод 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.1 1/3 3/7
Когда программа начинает выполнение, она передает метод вызова инициализации всем нашим классам. Если имеется класс и связанный с ним подкласс, родительский класс получает это сообщение первым. Это сообщение передается каждому классу только один раз, и оно гарантированно отправляется до того, как любые другие сообщения будут переданы классу. Ваша цель в этот момент — выполнение инициализации любого класса. Например, вам может потребоваться инициализация некоторых статических переменных, связанных с данным классом. 10.2. Снова об области действия
На область действия переменных в программе можно влиять разными способами: с переменными экземпляра или с обычными переменными, определенными вне или внутри функций. Ниже мы будем использовать термин модуль (module ) при ссылке на любое число определений методов или функций, содержащихся в одном исходном файле. Директивы для управления областью действия переменных экземпляра
Вы уже знаете, что переменные экземпляра имеют область действия, которая ограничивается методами экземпляра, определенными для данного класса. Поэтому любой метод экземпляра может выполнять доступ к свои переменным экземпляра по имени без дополнительных действий. Вы также знаете, что переменные экземпляра наследуются подклассом. Д оступ к переменным экземпляра тоже можно выполнять по имени из любого метода, определенного в этом подклассе. И в этом случае специальные действия тоже не требуются.
Перед переменными экземпляра при объявлении в секции interface можно помещать четыре директивы, чтобы более точно управлять их областью действия.
@protected. Методы, определенные в данном классе и любых подклассах, могут выполнять непосредственный доступ к последующим переменным экземпляра. Это вариант по умолчанию.
@private. Методы, определенные в данном классе (но не в подклассах), могут выполнять непосредственный доступ к последующим переменным экземпляра.
@public. Методы, определенные в данном классе и любых классах или модулях, могут выполнять непосредственный доступ к последующим переменным экземпляра.