3.@class指令
有如下代码:
#import <Foundation/ Foundation.h>
@class XYPoint
@interface Rectangle: NAObject
@property int width,height;
-(XYPoint* ) origin ;
-(void) setOrigin: (XYPoint* ) pt;
-(void) setWidth: (int) w andHeight: (int) h;
-(int) area;
-(int) perimeter;
@end
在这段代码中,有一行代码为@class XYPoint,这是因为编译器在遇到Rctangle定义的实例变量时,必须了解XYPoint是什么。类名会在origin 和setOrigin:方法的参数及返回类型声明。还有另一个选择,就是导入头文件,如:#import “XYPoint.h”。但是,使用@class指令提高了效率。因为编译器不需要导入和处理XYPoint.h的文件(虽然它很小),只需知道XYPoint是一个类名。如果需要引用XYPoint类的方法(在实现部分当中),@class指令不够,因为编译器需要更多的消息,需要了解方法的参数的多少,参数的类型,方法的返回类型。
4.以下是完整的XYPoint和Rectangle类定义:
XYPoint.h接口文件
#import <Foundation/Foundation.h>
-(void) setOirgin:(XYPoint *) pt
{
origin = pt;
}
-(XYPoint *) origin
{
return origin;
}
@end
XYPoint.m实现文件
#import "XYPoint.h"
@implementaton XYPoint
@synthesize x,y;
-(void) setX: (int) xVal andY: (int) yVal
{
x = xVal;
y = yVal;
}
@end
Rectangle.h接口文件
@class XYPoint;
@interface Rectangle: NSObject
@property int width,height;
-(XYPoint* ) origin ;
-(void) setOrigin: (XYPoint* ) pt;
-(void) setWidth: (int) w andHeight: (int) h;
-(int) area;
-(int) perimeter;
@end
Rectangle.m实现文件#import "Rectangle.h"
@implementation Rectangle
{
XYPoint *origin;
}
@synthesize width,height;
-(void) setWidth: (int) w andHeiget: (int) h
{
width = w;
height = h;
}
-(void) setOirgin:(XYPoint *) pt
{
origin = pt;
}
-(int) area
{
return width * height;
}
-(int) perimeter
{
return (width + height ) *2;
}
-(XYPoint *) origin
{
return origin;
}
@end
测试程序:
#import "Rectangle.h"
#import "XYPoint.h"
int main (int argc, char * argv[])
{
@autoreleasepool{
Rectangle *myRect = [Rectangle new];
XYPoint *myPoint = [XYPoint new ];
[myPoint setX: 100 andY: 200];
[myRect setWidth: 5 andHeight: 8];
myRect.origin = myPoint; //此语句调用setOrigin方法;
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);
}
return 0;
}
运行结果:
Origin at (100,200)
Origin at (50,50)
将myPoint的值改变后,也改变了矩形的原点。由于
-(void) setOirgin:(XYPoint *) pt
{
origin = pt;
}
这段代码的原因。当执行这段代码时,会产生如下的内存分布,
为了避免以上的情况,通常将setOrigin:方法改为
-(void) setOrigin: (XYPoint *) pt
{
if (! origin)
origin = [XYPoint new];
origin.x = pt.x;
origin.y = pt.y;
}
所有的实例变量在初始化时均为空。当创建一个新的Rectangle对象时,它的的实例变量(包括origin)将为空。如果origin为零,setOrigin:方法将创建和初始化一个新的XYPoint对象并储存在引用origin中,这意味着每一个Rectangle实例都有它的XYPoint实例。这个方法中引用了XYPoint类的一些实例变量,所以编译器需要的信息多于@class指令提供的。因此在Rectangle.h接口文件中需要用#import “XYPoint.h”;代替@class XYPoint;,导入XYPoint文件。
总结:当一个类中包含另一个类的对象的时候,通常在设置方法中要分配所包含的对象的空间。在默认情况下,使用合成的设置方法只是简单地复制对象的指针,而不是对象本身。
5.覆写方法
不能通过继承删除或减少方法,但是可以利用覆写来更改继承方法的定义。使用和父类相同的名称定义的方法代替或覆写了继承的定义。新方法必须具有相同的返回类型,并且参数的数目与覆写的方法相同。例如:
ClassA的声明和定义
#import <Foundation/Foundation.h>
@interface ClassA: NSObject
{
int x; //将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 char , char * argv[])
{
@autoreleasepool{
ClassB * b = [ [ClassB alloc] init];
[b initVar];
[b printVar];
}
return 0;
}
运行结果为:
x = 100
选择哪种方法:根据作为消息的接受者的类选择正确的方法。
当定义一个子类时,不仅可以添加新方法来有效地扩展类的定义,还可以添加新的实例变量。以上两种方法是累加的,不能通过继承减少方法或实例变量,只能添加。对于方法来说,可以添加或覆写。
之所以要创建子类,有如下三点;
1)希望继承一个类的函数,也许加入一些新的方法或实例变量。
2)希望创建一个类的特别版本(如图形对象的特定类型)
3)希望通过覆写一个活多个方法来改变类的默认行为。
6.抽象类
创建该类只是为了更容易创建子类,在该类中定义方法和实例变量,但不期望任何人从该类创建实例。