Шрифт:
Она требуется нам, поскольку компилятору нужно знать, что представляет собой XYPoint, когда он встречается в одной из переменных экземпляра, определенных для Rectangle. Имя этого класса используется также в объявлениях типов аргумента и возвращаемого значения для наших методов setOrigin: и origin соответственно. У вас есть и другой вариант выбора — импортировать файл заголовка, например, в следующем виде: #import "XYPoint.h"
Директива @class эффективнее, поскольку компилятору не нужно обрабатывать весь файл XYPoint.h (хотя это небольшой файл); компилятору достаточно знать, что XYPoint является именем класса. Если нужна ссылка на один из методов класса XYPoint, то директива @class недостаточна, поскольку компилятору потребуется дополнительная информация: аргументы, передаваемые методу, их типы, тип возвращаемого значения метода.
Заполним формы для нового класса XYPoint и новых методов класса Rectangle, чтобы протестировать их в программе. В программе 8.4 имеется файл секции implementation для класса XYPoint.
Сначала в ней показаны новые методы для класса Rectangle. #import "XYPoint.h" -(void) setOrigin: (XYPoint *) pt { origin = pt; -(XYPoint *) origin { return origin; ) @end
Затем показаны полные определения классов XYPoint и Rectangle и тестовая программа для их проверки. #import <Foundation/Foundation.h> @interface XYPoint: NSObject { int x; int y; } @property int х, у; -(void) setX: (int) xVal andY: (int) yVal; @end #import "XYPoint.h" @implementation XYPoint @synthesize x, y; -(void) setX: (int) xVal andY: (int) yVal { x = xVal; у = yVal; } @end #import <Foundation/Foundation.h> @class XYPoint; @interface Rectangle: NSObject { int width; int height; XYPoint *origin; } @property int width, height; -(XYPoint *) origin; -(void) setOrigin: (XYPoint *) pt; -(void) setWidth: (int) w andHeight: (int) h; -(int) area; -(int) perimeter; @end #import "Rectangle.h" @implementation Rectangle @synthesize width, height; -(void) setWidth: (int) w andHeight: (int) h width = w; height = h; -(void) setOrigin: (XYPoint *) pt origin = pt; -(int) area { return width * height; } -(int) perimeter { return (width + height) * 2; } -(Point *) origin { return origin; } @end #import "Rectangle.h" #import "XYPoint.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Rectangle *myRect = [[Rectangle alloc] init]; XYPoint * myPoint = [[XYPoint alloc] init]; [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 andHeight: 8]; myRect.origin = myPoint; NSLog (@"Rectangle w = %i, h = %i", myRect.width, myRect.height); NSLog (@"Origin at (%i, %i)"> myRect.origin.x, myRect.origin.y); NSLog (@»Area = %i, Perimeter = %i», [myRect area], [myRect perimeter]); [myRect release]; [myPoint release]; [pool drain]; return 0; }
Вывод программы 8.4 Rectangle (Прямоугольник) w = 5, h = 8 Origin at (Координаты начала) (100, 200) Area (Площадь) = 40, Perimeter (Периметр) = 26
Внутри процедуры main выделена память и инициализирован объект класса Rectangle с именем myRect и объект класса XYPoint с именем myPoint. С помощью метода setX:andY: объекту myPoint присваивается значение (100, 200). После задания ширины и высоты этого прямоугольника (5 и 8 соответственно) вызывается метод setOrigin, чтобы задать для координат начала прямоугольника точку, указанную в myPoint. Затем с помощью трех вызовов процедуры NSLog выполняется считывание и вывод этих значений. В выражении myRect.origin.x
считывается объект класса XYPoint, возвращенный методом доступа origin, и применяется оператор «точка» для получения координаты «х» начала прямоугольника. В следующем выражении считывается координата «у» начала прямоугольника: myRect.origin.y Классы, владеющие своими объектами
Можете ли вы объяснить результаты вывода программы 8.5? #import "Rectangle.h" #import "XYPoint.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Rectangle *myRect = [[Rectangle alloc] init]; XYPoint *myPoint = [[XYPoint alloc] init]; [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 and Height: 8]; myRect.origin = myPoint; NSLog (@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y); [myPoint setX: 50 andY: 50]; NSLog (@"Origin at (%i, %i)", myRect.origin.x, myRect.origin.y); [myRect release]; [myPoint release]; [pool drain]; return 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 присваивается значение координат х,у аргумента, передаваемого методу.