Программирование на Objective-C 2.0
Шрифт:
Примечание. Вы могли бы принудительно использовать этот метод, но мы не будем описывать способы обхода, поскольку это не соответствует практике надежного программирования.
Вернемся к примеру и добавим метод printVar в класс ClassA, чтобы вывести значение его переменных экземпляра. // Объявление и определение класса ClassA @interface ClassA: NSObject { int x; -(void) initVar; -(void) printVar; @end @implementation ClassA -(void) initVar { x = 100; } -(void) printVar { NSLog (@"x = %i", x); } @end
Объявление и определение класса ClassB остается без изменений. Запустим компиляцию и выполнение программы.
Вывод программы 8.7 х= 100 х = 200
а и b определены как объекты
Постарайтесь разобраться, как для переменных а и b происходит выбор метода, исходя из класса, которому они принадлежат. Это одна из базовых концепций объектно-ориентированного программирования в Objective-C.
В качестве упражнения попробуйте удалить метод printVar из класса ClassB. Получится ли это? Почему? Замещение метода dealloc и ключевое слово super
Теперь, когда вы знаете, как замещать методы, вернемся к программе 8.5В, чтобы изучить более подходящий способ освобождения памяти, выделенной для origin. Метод setOrigin: выделяет память для своего собственного объекта origin класса XYPoint, и вы обязаны освободить эту память. В программе 8.6 освобождение памяти выполнял оператор: [[myRect origin] release];
Вам не нужно заботиться об освобождении всех отдельных членов класса; вы можете заместить метод dealloc (наследуемый из NSObject) и освободить там память origin.
Примечание. Мы будем замещать метод dealloc, а не метод release. Метод release иногда освобождает память, используемую объектом, а иногда нет. Он освобождает память, используемую объектом, только если никто другой не обращается к этому объекту, и делает это, вызывая метод объекта dealloc, который фактически освобождает память.
При замещении метода dealloc вы должны проследить, чтобы была освобождена память, занимаемая не только вашими переменными экземпляра, но и всеми унаследованными переменными.
Для этого существует специальное ключевое слово super, которое обозначает родительский класс получателя сообщения. Для выполнения замещаемого метода нужно передать super сообщение. Выражение с сообщением [super release];
при использовании внутри метода вызывает метод release, который определен (или унаследован) в родительском классе. Этот метод вызывается в получателе сообщения, то есть в себе самом (self).
Таким образом, замещение метода dealloc для класса Rectangle выполняется следующим образом. Сначала освобождается память, занятая origin, а затем вызывается метод dealloc из родительского класса. Тем самым освобождается память, занятая самим объектом Rectangle. Ниже приводится этот метод. -(void) dealloc { if (origin) [origin release]; [super dealloc]; }
Определенный здесь метод dealloc не возвращает никакого значения. Внутри метода сначала dealloc выполняется проверка, что origin имеет ненулевое значение. Начало прямоугольника (origin), возможно, не было задано. В этом случае он имеет по умолчанию нулевое значение. Затем вызывается метод dealloc из родительского класса, который был бы унаследован классом Rectangle, если бы не был замещен.
Метод dealloc можно написать проще: -(void) dealloc { [origin release]; [super dealloc]; }
поскольку
вы можете без проблем передать сообщение nil-объекту. Кроме того, к origin применяется release, а не dealloc. В любом случае, если никто другой не использует origin, release вызовет метод dealloc для origin, чтобы освободить его пространство. Используя этот метод, вы должны освобождать только те объекты-прямоугольники, для которых выделили память, не заботясь об объектах XYPoint, которые они содержат. Двух сообщений, показанных в программе 8.5, теперь достаточно, чтобы освободить память для всех объектов, в том числе объекта XYPoint, создаваемого с помощью setOrigin:. [myRect release]; [myPoint release];Правда, остается одна проблема. Если задать для начала прямоугольника (origin) одного объекта Rectangle другие значения во время выполнения программы, то вы должны освободить память, занятую прежним началом прямоугольника, прежде чем выделить и назначить новую. Рассмотрим следующую последовательность строк: myRect.origin = startPoint; myRect.origin = endPoint; [startPoint release]; [endPoint release]; [myRect release];
Копия объекта startPoint класса XYPoint, сохраненная в элементе origin myRect, не будет освобождена, поскольку она перезаписывается вторым значением для origin (endPoint). Эта копия origin будет освобождена правильно, когда будет освобождаться сам объект прямоугольника, если применяется новый метод освобождения памяти.
Но вы должны сделать так, чтобы память, выделенная для предыдущего начала (origin), была освобождена до того, как будет задано новое начало. Это можно сделать в методе setOrigin:. -(void) setOrigin: (XYPoint *) pt { if (origin) [origin release]; origin = [[XYPoint alloc] init]; [origin setX: pt.x andY: pt.y]; }
Если вы синтезируете свои методы доступа, то можете сделать так, чтобы компилятор автоматически разрешил эту проблему. 8.4. Расширение через наследование: добавление новых переменных экземпляра
Вы можете добавлять не только новые методы, расширяя определение класса, но и новые переменные экземпляра. В обоих случаях получается накопительный эффект. При наследовании вы не можете удалять методы или переменные экземпляра. Переменные вы можете только добавлять, а методы— добавлять или замещать.
Вернемся к простым классам ClassA и ClassB и внесем некоторые изменения. Добавим в ClassB новую переменную экземпляра у. @interface ClassB: ClassA { int у; } -(void) printVar; @end
Может показаться, что ClassB будет иметь только одну переменную экземпляра (с именем у), но, исходя из предыдущего объявления, на самом деле он имеет две. Он имеет его собственную переменную экземпляра у и наследует переменную х из класса ClassA.
Примечание. Этот класс имеет также переменные экземпляра, которые наследует из класса NSObject, но мы пока будет игнорировать этот факт. Ниже приводится простой пример этой концепции (программа 8.8). // Расширение переменных экземпляра #import <Foundation/Foundation.h> // Объявление и определение ClassA @interface ClassA: NSObject { int x; } -(void) initVar; @end @implementation ClassA -(void) initVar { x = 100; } @end // Объявление и определение ClassB @interface ClassB: ClassA { int у; } -(void) initVar; -(void) printVar; @end @implementation ClassB -(void) initVar x = 200; у = 300; ) -(void) printVar { NSLog (@"x = %i", x); NSLog (@"y = %i", y); } @end int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassB *b = [[ClassB alloc] init]; [b initVar]; // uses overriding method in ClassB [b printVar]; // reveal values of x and y; [b release]; [pool drain]; return 0; }