Программирование на Objective-C 2.0
Шрифт:
Сначала выполняется оценка внутреннего выражения: [Fraction alloc]
Результатом этого выражения является конкретный выделяемый элемент типа Fraction. Вместо сохранения результата выделения в переменной, как это было сделано выше, к нему непосредственно применяется метод init. Таким образом, здесь снова выделяется сначала новый элемент типа Fraction, и затем происходит его инициализация. Результат инициализации присваивается переменной myFraction.
Для сокращения записи выделение памяти и инициализация часто встра-иваются непосредственно в строку объявления: Fraction *myFraction = [[Fraction alloc] init];
Мы часто используем этот стиль в книге, поэтому важно, чтобы вы понимали его. Вы видели выше, как действует автоматически
Здесь сообщение alloc отправляется классу NSAutoreleasePool для запроса о создании нового экземпляра. Затем этому новому созданному объекту отправ-ляется сообщение init для его инициализации.Теперь, вернувшись к программе 3.2, вы можете задать значение дроби. // Set fraction to 1/3 [myFraction setNumerator: 1]; [myFraction setDenominator: 3];
В первом операторе выполняется отправка сообщения setNumerator: переменной myFraction. Для аргумента указывается значение 1, и управление передается методу setNumerator:, который был определен для класса Fraction. Система Objective - С «понимает», что это метод изданного класса, поскольку «знает», что myFraction является объектом из класса Fraction.
Внутри метода setNumerator: переданное значение 1 сохраняется в перемен-ной п. В единственной программной строке метода это значение сохраняется в переменной экземпляра numerator. Фактически вы присваиваете элементу numerator в myFraction значение 1.
Затем следует сообщение для обращения к методу setDenominator: в myFraction. Значение аргумента 3 присваивается переменной d внутри метода setDenominator:. Это значение затем сохраняется в переменной экземпляра denominator, что завершает присваивание значения 1/3 переменной myFraction. Теперь можно вывести на экран значение дроби. // display the fraction using the print method NSLog (@The value of myFraction is:"); [myFraction print];
В результате вызова NSLog выводится текст: The value of myFraction is: (Значение myFraction:)
В следующем выражении для сообщения вызывается метод print: [myFraction print];
Внутри метода print выполняется вывод значений переменных экземпляра numerator и denominator, разделенных наклонной чертой.
Следующее сообщение в программе освобождает память, которая исполь-зовалась для этого объекта Fraction: [myFraction release];
Это критически важная часть в практике программирования. При создании каждого нового объекта вы запрашиваете память, выделяемую для объекта. За-кончив работу с объектом, вы обязаны освободить эту память. Память осво-бождается автоматически, когда происходит завершение программы, но при разработке сложных приложений вам придется работать с сотнями (или тыся-чами) объектов, которые используют много памяти. Если не освобождать память во время работы программы, то это может замедлить ее выполнение. Поэтому возьмите за правило освобождать память, как только это можно сделать.
В системе выполнения программ (runtime) Apple обеспечивается механизм очистки памяти, который называется сборкой мусора (garbage collection), но луч-ше самому управлять использованием памяти, а не полагаться на этот автома-тизированный механизм.
Вы не можете полагаться на сборку мусора для тех платформ, где она не поддерживается, например, iPhone. У вас может создаться впечатление, что вам пришлось написать намного больше кода, чтобы сделать в программе 3.2 то же самое, что и в программе 3.1. Для данного простого примера это верно, но такой способ делает упрощает написание, поддержку и расширение больших и сложных программ. Вы поймете это позже.
В последнем примере этой главы показано, как работать с несколькими дро-бями. В программе 3.3 одной дроби присваивается значение 2/3, второй дроби — 3/7, и затем выполняется вывод обеих дробей. // Program to work with fractions - cont’d (Программа для работы с дробями - продолжение) #import <Foundation/Foundation.h> //---- @interface section ---- @interface Fraction: NSObject { int numerator; int denominator; } -(void) print; -(void) setNumerator: (int) n; -(void) setDenominator: (int) d; @end //---- @implementation section ---- @implementation Fraction -(void) print { NSLog (@"%i/%i", numerator, denominator); } -(void) setNumerator: (int) n { numerator = n; ) -(void) setDenominator: (int) d { denominator = d; } @end //---- program section ---- int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *frac1 = [[Fraction alloc] init]; Fraction *frac2 = [[Fraction alloc] init]; // Set 1st fraction to 2/3 (Задание 1 -й
дроби 2/3) [frac setNumerator: 2]; [frac setDenominator: 3]; // Set 2nd fraction to 3/7 (Задание 2-й дроби 3/7) [frac2 setNumerator: 3]; [frac2 setDenominator: 7]; // Display the fractions (Вывод дробей) NSLog (@"First fraction is:"); [frac print]; NSLog (@"Second fraction is:"); [frac2 print]; [frac release]; [frac2 release]; [pool drain]; return 0; }Вывод программы 3.3 First fraction is: (Первая дробь:) 2/3 Second fraction is: (Вторая дробь:) 3/7
Секции @interface и @implementation остались такими же, как в программе 3.2. В программе создаются два объекта типа Fraction, — frad и frac2, затем им присва-иваются соответственно значения 2/3 и 3/7. Когда метод setNumerator: применяется к frad, чтобы задать значение 2 для его числителя (numerator), выполняется присваивание значения 2 переменной экземпляра numerator. Аналогичным об-разом, когда для frac2 применяется тот же метод, чтобы задать значение 3 для его числителя, выполняется присваивание значения 3 его отдельной переменной экземпляра numerator. Новый объект получает свой собственный отдельный набор переменных экземпляра при каждом создании (рис. 3.2).
В зависимости от объекта, которому отправляется сообщение, происходит ссылка на соответствующие переменные экземпляра. В следующем примере выполняется ссылка на numerator объекта fraci, если внутри метода setNumerator: используется имя numerator: [trad setNumerator: 2];
Это происходит потому, что получателем сообщения является frac1.
Рис. 3.2. Уникальные переменные экземпляра 3.7. Доступ к переменным экземпляра и инкапсуляция данных
Мы увидели, каким образом методы, используемые для работы с дробями, вы-полняют доступ по имени к двум переменным экземпляра: numerator и denominator. Метод экземпляра всегда может выполнять непосредственный доступ к переменным экземпляра. Однако это не может делать метод класса, поскольку он применяется только к самому классу, а не к экземплярам этого класса. Но что делать, если нужно выполнять доступ к переменным экземпляра из какого-либо другого места, например, изнутри процедуры main? Это нельзя сделать напрямую, поскольку переменные экземпляра скрыты. Это еще одна ключевая концепция, которая называется инкапсуляцией данных (data encapsulation). Это позволяет расширять и изменять определения класса, не заботясь о том, что пользователи данного класса будут работать с внутренними деталями класса. Инкапсуляция данных обеспечивает необходимый уровень изоляции между программистом и разработчиком класса.
Вы можете выполнять доступ к свои переменным экземпляра, написав спе-циальные методы для считывания их значений. Например, можно создать два новых метода (numerator и denominator) для доступа к соответствующим переменным экземпляра Fraction, который является получателем сообщения. Результатом будет возвращаемое целое значение. Ниже приводятся объявления для двух новых методов: -(int) numerator; -(int) denominator;
Их определения: -(int) numerator { return numerator; } -(int) denominator { return denominator; }