Программирование на Objective-C 2.0
Шрифт:
Предположим, что вы добавили эти объявления и определения для нового метода add: в файлы секций interface и implementation. Ниже приводится пример тестовой программы 7.3 и ее вывода. #import «Fraction.h» int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *aFraction = [[Fraction alloc] init]; Fraction *bFraction = [[Fraction alloc] init]; // Задание дробей 1/4 и ? и их сложение [aFraction setTo: 1 over: 4]; [bFraction setTo: 1 over: 2]; // Вывод результатов [aFraction print]; NSLog (@"+"); [bFraction print]; NSLog (@"=n); [aFraction add: bFraction]; [aFraction print]; [aFraction release]; [bFraction release]; [pool drain]; return 0; }
Вывод
Тестовая программа выглядит достаточно просто. Выделяется память и инициализируются два объекта класса Fraction: aFraction и bFraction. Им присваиваются значения 1/4 и 1/2 соответственно, затем дробь bFraction прибавляется к дроби aFraction и результата сложения выводится. Метод add: прибавляет аргумент к объекту сообщения, изменяя этот объект. Это проверяется, когда происходит вывод значения aFraction в конце процедуры main. Можно было бы вывести значение aFraction до вызова метода add:, чтобы увидеть значение до того, как оно было изменено с помощью метода. Ниже в этой главе мы переопределим метод add:, чтобы он не влиял на значение своего аргумента. 7.5. Локальные переменные
Вы, вероятно, заметили, что результат сложения 1/4 и 1/2 выведен как 6/8, а не 3/4. Наша процедура сложения выполняет только заданные действия, в ней не предусмотрено сокращение дроби. Добавим новый метод, с помощью которого выполняется сокращение дробей.
Сокращение дроби означает, что нужно найти наибольшее число, на которое делятся без остатка числитель и знаменатель этой дроби, и выполнить деление на это число. Это действие называется поиском наибольшего общего делителя (greatest common divisor, gcd) числителя и знаменателя. Вы уже знаете, как это делать, из программы 5.7. Используя этот алгоритм, напишем метод сокращения дроби (reduce). -(void) reduce { int u = numerator; int v = denominator; int temp; while (v != 0) { temp = u % v; u = v; v = temp; } numerator /= u; denominator /= u; }
В этом методе объявляются три целые переменные u, v и temp, которые являются локальными. Это означает, что они существуют только во время выполнения метода reduce и доступ к ним может выполняться только внутри метода, в котором они определены. В этом смысле они аналогичны переменным, объявленным в процедуре main; эти переменные тоже были локальными по отношению к main, и доступ к ним мог выполняться только внутри процедуры main. Ни один из написанных вами методов не мог иметь непосредственного доступа к этим переменным, определенным в main.
Локальные переменные не имеют начального значения по умолчанию, поэтому вы должны присвоить им некоторое значение, прежде чем использовать их. Трем локальным переменным в методе reduce присваиваются значения до того, как они используются, поэтому здесь не возникает никакой проблемы. В отличие от переменных экземпляра (которые сохраняют свои значения при вызовах методов), эти локальные переменные не имеют памяти, и после возврата из метода значения этих переменных исчезают. Каждый раз, когда происходит вызов метода, выполняется инициализация каждой локальной переменной, определенной в этом методе: ей присваивается значение, указанное при объявлении переменной. Аргументы для метода
Имена, которые вы используете для обозначения аргументов метода, тоже являются локальными переменными. При выполнении метода любые аргументы, передаваемые методу, копируются в эти переменные. Поскольку метод работает с копией аргументов, он не может изменить исходные
значения, переданные методу. Предположим, у нас есть метод с именем calculate:, определенный следующим образом: -(void) calculate: (double) х { х *= 2; }Предположим также, что для его вызова использовано выражение: [myData calculate: ptVal];
При вызове метода calculate значение, содержащееся в переменной ptVal, копируется в локальную переменную х. Поэтому изменение х внутри calculate: не оказывает никакого влияния на значение переменной ptVal — только копия ее значения сохраняется внутри х.
Для аргументов, которые являются объектами, вы можете изменять переменные экземпляра, хранящиеся в этом объекте. Об этом рассказывается в следующей главе. Ключевое слово static
Чтобы локальная переменная сохраняла свое значение при нескольких вызовах метода, поместите ключевое слово static перед объявлением переменной. В следующей строке целая переменная hitCount объявляется как статическая переменная. static int hitCount = 0;
В отличие от других локальных переменных, статическая переменная имеет начальное значение 0, поэтому приведенная выше инициализация является излишней. Кроме того, статические переменные инициализируются только один раз, в начале выполнения программы, и сохраняют свои значения при нескольких вызовах метода.
Приведенная ниже последовательность строк может содержаться внутри метода showPage, который должен следить за числом своих вызовов (в данном случае — число выведенных страниц). -(void) showPage { static int pageCount = 0; ++pageCount;
Локальной статической переменной присваивается значение 0 только один раз (при запуске программы), а затем это значение сохраняется при всех последующих вызовах метода showPage.
Если pageCount задана как локальная статическая переменная, то в ней подсчитывается число страниц, выведенных всеми объектами, которые вызвали метод showPage.
Если pageCount задана как переменная экземпляра, то в ней подсчитывается число страниц, выведенных каждым объектом, поскольку каждый объект будет содержать свою собственную копию pageCount.
Доступ к статическим или локальным переменным может выполняться только из метода, где они определены, поэтому даже статическая переменная pageCount доступна только внутри showPage. Чтобы сделать эту переменную доступной другим методам, нужно объявить ее вне объявления любого метода (обычно это делают в начале файла implementation), например: #import "Printer.h" static int pageCount; @implementation Printer @end
После этого любой метод экземпляра или класса сможет выполнять доступ к переменной pageCount. Области действия переменных подробно рассматриваются в гл. 10.
Итак, мы можем вставить код метода reduce в файл секции implementation Fraction.m. Не забудьте объявить метод reduce в файле секции interface Fraction.h.
После этого вы можете проверить метод в программе 7.4. #import «Fraction.h» int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *aFraction = [[Fraction alloc] init]; Fraction *bFraction = [[Fraction alloc] init]; [aFraction setTo: 1 over: 4]; // set 1st fraction to 1/4 [bFraction setTo: 1 over: 2]; // set 2nd fraction to 1/2 [aFraction print]; NSLog (@"+"); [bFraction print]; NSLog (@"="); [aFraction add: bFraction]; // сокращение дроби после сложения и вывод результата [aFraction reduce]; [aFraction print]; [aFraction release]; [bFraction release]; [pool drain]; return 0; }