Чтение онлайн

на главную - закладки

Жанры

Программирование на Objective-C 2.0
Шрифт:

Вывод программы 8.5 Origin at (Координаты начала) (100, 200) Origin at (50, 50)

В этой программе значение объекта myPoint было изменено с (100, 200) на (50, 50), то есть были изменены координаты начала прямоугольника. Но почему это произошло? Здесь не было явным образом задано новое значение начала прямоугольника, почему оно изменилось? Вернемся к определению метода setOrigin:, чтобы понять причину: -(void) setOrigin: (XYPoint *) pt { origin = pt; }

При вызове метода setOrigin: с помощью выражения myRect.origin = myPoint;

значение myPoint передается этому методу как аргумент. Это значение указывает место в памяти, где хранится данный объект XYPoint (рис. 8.5).

Рис. 8.5. Объект myPoint класса XYPoint в памяти

Это значение, сохраненное в myPoint и являющееся указателем места в памяти, копируется в локальную переменную pt, определенную внутри метода. После этого pt и myPoint являются ссылкой на одни и те же данные, хранящиеся в памяти (рис. 8.6).

Рис. 8.6. Передача методу информации о начале прямоугольника

Когда переменной origin присваивается pt внутри этого метода, указатель, хранящийся внутри pt, копируется в переменную экземпляра origin (рис. 8.7).

Рис. 8.7. Задание начала (origin) прямоугольника

Поскольку myPoint и переменная origin, хранящаяся в myRect, ссылаются на одну и ту же область в памяти (как и локальная переменная pt), при последующем изменении значения myPoint на (50,50) изменяется и значение начала прямоугольника.

Чтобы избежать этой проблемы, нужно модифицировать метод setOrigin: так, чтобы он выделял (alloc) свою собственную точку и присваивал началу прямоугольника (origin) эту точку. -(void) setOrigin: (XYPoint *) pt { origin = [[XYPoint alloc] init]; [origin setX: pt.x andY: pt.y]; }

Метод сначала выделяет память и инициализирует новый объект класса XYPoint. В выражении для сообщения [origin setX: pt.x andY: pt.y];

новому объекту класса XYPoint присваивается значение координат х,у аргумента, передаваемого методу.

Это изменение в методе setOrigin: означает, что теперь каждый экземпляр Rectangle владеет свои собственным экземпляром XYPoint. Теперь он не только осуществляет выделение памяти для XYPoint, но и освобождает эту память. Если класс содержит другие объекты, бывает нужно, чтобы он владел некоторыми или всеми объектами. Для прямоугольника класс Rectangle должен владеть объектом начала (origin) прямоугольника, поскольку это один из основных атрибутов.

Но была ли освобождена память, которая использовалась для origin? Освобождение памяти, занятой для прямоугольника (myRect), не освобождает память, которая была выделена для начала прямоугольника (origin). Чтобы освободить эту память, нужно вставить в main строку [[myRect origin] release];

В результате будет освобожден объект XYPoint, возвращаемый методом origin. Вы должны сделать это до того, как освободите память для самого объекта Rectangle, поскольку ни одна из переменных, содержащихся в этом объекте, недействительна после того, как освобождена память объекта. Необходима следующая последовательность строк кода. [[myRect origin] release]; // Освобождение памяти для origin [myRect release]; // Освобождение памяти для прямоугольника

Вы вынуждены помнить, что нужно освобождать память непосредственно для origin, хотя не вы выделяли эту память; это сделал класс Rectangle. В следующем разделе, «Замещающие методы», вы узнаете, как сделать, чтобы Rectangle освобождал память.

После перекомпиляции и перезапуска программы 8.5 с модифицированным методом появляются сообщения об ошибках (рис. 8.8).

Рис. 8.8. Сообщения компилятора об ошибках

Проблема возникает из-за того, что мы использовали в модифицированном методе некоторые методы из класса XYPoint, и теперь компилятору требуется больше информации об этом классе, чем дает директива @class. Нужно вернуться назад и заменить эту директиву импортом: #import "XYPoint.h

Вывод программы 8.5В Origin at (100, 200) Origin at (100, 200)

Это уже лучше. Теперь изменение значения myPoint на (50, 50) внутри main не окажет никакого влияния на координаты начала прямоугольника, поскольку копия этой точки была создана внутри метода setOrigin: объекта Rectangle. Мы не синтезировали здесь методы origin, поскольку синтезированный метод-установщик setOrigin: будет действовать точно так же, как метод, написанный нами первоначально. По умолчанию синтезированный метод-установщик просто копирует указатель объекта, а не сам объект.

Вы можете синтезировать другой тип метода-установщик, который создает копию объекта, но для этого вам нужно научиться писать копирующий метод. Мы вернемся к этой теме в главе 17. 8.3. Замещающие методы

Выше уже говорилось, что мы не можем удалить или обойти методы при наследовании, но можем изменить определение наследуемого метода путем замещения. Возвращаясь к двум классам, ClassA и ClassB, предположим, что нужно написать собственный метод initVar для ClassB. Мы уже знаем, что ClassB будет наследовать метод initVar, определенный в классе ClassA, но можно ли создать новый метод с тем же именем для замены наследуемого метода? Да, можно, для этого нужно просто определить новый метод с тем же именем. Метод, определенный с таким же именем, как в родительском классе, заменяет, или замещает (override), унаследованное определение. Новый метод должен иметь такой же тип возвращаемого значения и принимать такое же число аргументов такого же типа, как метод, который вы замещаете.

В программе 8.6 показан простой пример, отражающий эту концепцию. // Замещающие методы #import <Foundation/Foundation.h> // Объявление и определение класса ClassA @interface ClassA: NSObject { int x; -(void) initVar; @end @implementation ClassA -(void) initVar { x = 100; } @end // Объявление и определение класса ClassB @interface ClassB: ClassA -(void) initVar; -(void) printVar; @end @implementation ClassB -(void) initVar // добавляемый метод { x = 200; -(void) printVar { NSLog (@"x = %i", x); @end int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassB *b = [[ClassB alloc] init]; [b initVar]; // использование замещающего метода в В [b printVar]; // раскрытие значения х; [b release]; [pool drain]; return 0; }

Поделиться:
Популярные книги

С чего все началось?

Katelyn
Любовные романы:
современные любовные романы
3.40
рейтинг книги
С чего все началось?

Кодекс Охотника XXVIII

Винокуров Юрий
28. Кодекс Охотника
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Кодекс Охотника XXVIII

Темпус

Шмелев Дмитрий
1. Темпус
Фантастика:
научная фантастика
5.00
рейтинг книги
Темпус

Схрон

Головачев Василий Васильевич
2. Смутное время
Фантастика:
научная фантастика
8.08
рейтинг книги
Схрон

Обломов

Гончаров Иван Александрович
Проза:
классическая проза
русская классическая проза
8.78
рейтинг книги
Обломов

Барбаросса

Пикуль Валентин Саввич
Проза:
историческая проза
8.88
рейтинг книги
Барбаросса

Я еще барон. Книга III

Дрейк Сириус
3. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я еще барон. Книга III

Студент из прошлого тысячелетия

Еслер Андрей
2. Соприкосновение миров
Фантастика:
героическая фантастика
попаданцы
аниме
5.25
рейтинг книги
Студент из прошлого тысячелетия

Собрание сочинений. Т. 20. Плодовитость

Золя Эмиль
Проза:
классическая проза
5.00
рейтинг книги
Собрание сочинений. Т. 20. Плодовитость

Шайтан Иван 6

Тен Эдуард
6. Шайтан Иван
Фантастика:
попаданцы
альтернативная история
историческое фэнтези
7.43
рейтинг книги
Шайтан Иван 6

Новая Модель Вселенной

Успенский Петр Демьянович
Научно-образовательная:
психология
религиоведение
5.00
рейтинг книги
Новая Модель Вселенной

Тень Нерона

Алферова Марианна Владимировна
3. Сыщик
Фантастика:
детективная фантастика
6.25
рейтинг книги
Тень Нерона

Романов. Том 1 и Том 2

Кощеев Владимир
1. Романов
Фантастика:
фэнтези
попаданцы
альтернативная история
5.25
рейтинг книги
Романов. Том 1 и Том 2

Дань псам

Эриксон Стивен
8. «Малазанская империя»
Фантастика:
фэнтези
5.80
рейтинг книги
Дань псам