Программирование на Objective-C 2.0
Шрифт:
Первая строка определения этого метода: -(Fraction *) add: (Fraction *) f;
Она указывает, что метод add: будет возвращать объект класса Fraction и принимать такой объект в качестве аргумента. Этот аргумент будет складываться с получателем сообщения, которым тоже является объект класса Fraction.
Метод add: выделяет память и инициализирует новый объект типа Fraction с именем result и затем определяет две локальные переменные с именами resultNum и resultDenom. Они будут использоваться для сохранения числителей и знаменателей, получаемых в результате сложения.
После выполнения сложения (как и раньше) и присваивания значений числителей и знаменателей локальным переменным
После сокращения result (на наибольший общий делитель) его значение возвращается отправителю сообщения с помощью оператора return.
Отметим, что занимаемая объектом result память, которая выделена внутри метода add:, возвращается этим методом и не освобождается. Вы не можете освободить ее из метода add:, поскольку она требуется отправителю сообщения.
Поэтому пользователь этого метода обязан знать, что возвращаемый объект является новым экземпляром и должен быть в дальнейшем освобожден. Это можно указать в соответствующей документации, которая предоставляется пользователям данного класса.
В программе 7.5 выполняется проверка нового метода add:. #import «Fraction.h» int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *aFraction = [[Fraction alloc] init]; Fraction *bFraction = [[Fraction alloc] init]; Fraction ResultFraction; [aFraction setTo: 1 over: 4]; // присваивание значения 1/4 первой дроби [bFraction setTo: 1 over: 2]; // присваивание значения 1/2 второй дроби [aFraction print]; NSLog (@"+"); [bFraction print]; NSLog resultFraction = [aFraction add: bFraction]; [resultFraction print]; // Непосредственный вывод результата, см. ниже, // вызовет "утечку" памяти! [[aFraction add: bFraction] print]; [aFraction release]; [bFraction release]; [resultFraction release]; [pool drain]; return 0; }
Вывод программы 7.5 1/4 + 1/2 3/4 3/4
Сначала определяются два объекта типа Fraction: aFraction и bFraction, которым присваиваются значения 1/4 и 1/2 соответственно. Здесь также определяется объект Fraction с именем resultFraction (для которого не нужно выполнять выделение памяти и инициализацию). В этой переменной будет сохраняться результат последующих операций сложения.
В следующих строках кода сначала выполняется отправка сообщения add: получателю aFraction с передачей bFraction в качестве аргумента метода. resultFraction = [aFraction add: bFraction]; [resultFraction print];
Результирующий объект типа Fraction, возвращаемый методом, сохраняется в resultFraction и затем выводится путем передачи сообщения print. Отметим, что вы должны аккуратно завершить эту программу, освободив (release) resultFraction, хотя вы не выделяли для нее память (allocate) в main. Код выполнил метод add:, но освободить память должны вы сами. Следующее сообщение кажется очень удобным, но на самом деле здесь возникает проблема. [[aFraction add: bFraction] print];
Поскольку здесь берется объект типа Fraction, возвращаемый методом add:, и ему передается сообщение print, у вас нет никакого способа освободить этот. Мы видим здесь пример утечки памяти (memory leakage). При многократном повторении вложенной передачи сообщения такого рода у вас будет понемногу накапливаться память, которую вы не сможете непосредственным образом освободить.
Чтобы решить эту проблему, можно сделать так, чтобы метод print возвращал своего получателя, и вы могли бы его освободить. Но это обходной способ. Проще разбить эти вложенные сообщения на два отдельных сообщения, как сделано выше.
Мы могли бы избежать использования временных переменных resultNum
Мы не предлагаем писать такой сокращенный код, но вы можете встретить его у других программистов, поэтому нужно уметь читать и понимать подобные сложные выражения.
Рассмотрим еще один (последний в этой главе) пример, связанный с дробями. Это суммирование последовательности. ± 1/2 i= I
Оно означает, что нужно сложить значения 1/2i, где i изменяется от 1 до п, то есть сложить 1/2 + 1/4 + 1/8 .... Если сделать значение п достаточно большим, сумма будет приближаться к 1. Мы опробуем несколько значений п, чтобы посмотреть, насколько мы приблизимся к 1.
Программа 7.6 запрашивает ввод значения п и выполняет указанные вычисления. #import Traction.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *aFraction = [[Fraction alloc] init]; Fraction *sum = [[Fraction alloc] init], *sum2; int i, n, pow2; [sum setTo: 0 over: 1]; // присваивание значения 0 первой дроби NSLog (@"Enter your value for n:"); scant ("%i", &n); pow2 = 2; for (i = 1; i <= n; ++i) { [aFraction setTo: 1 over: pow2]; sum2 = [sum add: aFraction]; [sum release]; // освобождение памяти предыдущей суммы sum = sum2; pow2 *= 2; NSLog (@"After %i iterations, the sum is %g", n, [sum convertToNum]); [aFraction release]; [sum release]; [pool drain]; return 0; }
Вывод программы 7.6 Enter your value for n: (Введите значение n) 5 After 5 iterations, the sum is 0.96875 (По 5 итерациям сумма равна ...) Вывод программы 7.6 (Повторный запуск) Enter your value for n: 10 After 10 iterations, the sum is 0.999023 Вывод программы 7.6 (Повторный запуск) Enter your value for n: 15 After 15 iterations, the sum is 0.999969
Переменной sum типа Fraction присваивается значение 0: числитель (numerator), равный 0, и знаменатель (denominator), равный 1. (А что произойдет, если задать числитель и знаменатель, равные 0?). Затем программа запрашивает у пользователя ввод значения п и считывает его с помощью scant. Затем начинается цикл for для вычисления суммы последовательности. Перед циклом переменной pow2 присваивается значение 2. Эта переменная используется для сохранения значения 2i. На каждом шаге цикла это значение умножается на 2.
Цикл for начинается с 1 и продолжается до значения п. На каждом шаге цикла aFraction присваивается значение 1/pow2, то есть 1/2i. Это значение затем добавляется к сумме (sum) с помощью определенного ранее метода add:. Значение result, возвращаемое из add:, присваивается sum2, а не sum, чтобы избежать проблем утечки памяти. (А что произойдет, если присвоить result непосредственно sum?) Старое значение sum затем освобождается, и для следующего шага цикла sum присваивается новое значение суммы — sum2. Изучите, каким образом здесь происходит освобождение памяти для дробей. Для цикла for, который выполняется сотни или тысячи раз, при неверном освобождении памяти для дробей у вас быстро накопится большой объем неиспользуемой памяти.
По окончании цикла for конечный результат выводится в виде десятичного значения с помощью метода convertToNum. Остается только освободить два объекта: aFraction и конечный объект типа Fraction, сохраненный в sum. После этого завершается выполнение программы.
Результаты вывода показывают, что происходит при запуске программы. При значении 5 сумма последовательности равна 0.96875. При 15 результат очень близок к 1. Расширение определений класса и файл секции interface