[url=http://sarin.iteye.com/blog/1768779]接上文[/url]
之前定义了矩形类Rectangle,那么我们如果要在桌面上生成这样一个矩形,就需要定位了。为了简便,我们定义桌面的左下角为直角坐标系(笛卡尔坐标系)的原点,横向向右为X轴正向,竖向向上为Y轴正向。那么我们只要确定了矩形的左下角坐标就可以得到矩形的位置了。此时我们就要引入坐标的概念,那么设计XYPoint类,代码如下:
XYPoint.h文件定义了坐标类XYPoint的接口信息,这里面我们使用整数作为坐标,暂时不考虑小数坐标点。那么提供一个方法来设置坐标点,其实现代码为:
就是给属性x和y进行赋值,没有什么可多说的。因为我们要为矩形设置原点坐标(矩形左下角坐标),那么就需要对矩形类Rectangle进行修改,代码如下:
这是类的接口文件,这里面我们使用了@class指令来指定XYPoint类,@class指令可以为我们指定要使用的类,而不用使用import语句,因为这里我们只需要引入XYPoint的定义而已。如果要引用类的实现部分,那么必须使用import语句,@class就不足以提供所需内容了。同时矩形类加入了两个方法,一个是设置原点origin坐标,一个是获取原点坐标,那么矩形类的实现就修改如下:
我们定义私有属性origin来表示坐标原点,提供了设置方法和获取方法,这就没什么可多说的了,最后来看看主函数,该如何使用它们:
主函数中需要引入两个头文件,因为使用到了它们。创建一个矩形变量和一个坐标变量,对它们赋值后,将坐标原点设置给矩形对象,那么此时矩形对象就拥有了坐标原点,之后我们打印出它们的值,编译运行后得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0731/a1619663-9a93-33b1-ae1e-5e2cc273355a.png[/img]
我们修改一下主函数,代码如下:
这里只是对原点进行了二次赋值,那么编译运行后,我们得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0733/561bb550-a734-3e10-b929-2eed3fdd3b61.png[/img]
为什么会得到这样的结果?我们并没有显式的再次设置矩形的原点,只是对原点对象重新赋值后,矩形的原点也发生了相应的变化。我们来仔细看一下代码,调用setOrigin方法时,point作为参数传递给该方法,这个值是指针对象,指向了XYPoint对象的内存地址。我们使用rect.origin=point将地址赋值给矩形的原点指针上。因为这样赋值的特性,矩形中的原点和point指向的同一内存空间,那么我们修改了point的值,矩形的origin当然也会跟着改变。那么为了避免这个问题,我们修改setOrigin方法的实现,代码如下:
但是我们却得到了如下错误:
[img]http://dl.iteye.com/upload/attachment/0081/0735/3bb2195e-4c5a-30c0-821d-b3e17283efed.png[/img]
这是因为在Rectangle.h中我们使用@class指令来标识XYPoint,而现在需要XYPoint的细节,那么就需要修改头文件,将@class指令改为#import即可。之后修改主函数如下:
编译运行,得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0737/e0a17c3c-f575-3553-9024-504a729bcee8.png[/img]
这样就很合理了,原点就属于矩形自己的,称为它的一个属性了,再次修改坐标点不会对已有原点产生影响。但是问题又产生了,修改主函数如下:
如果我们在这里又定义一个对象来获取矩形的原点,然后对其重新赋值,那么我们得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0739/1502da61-3b24-3a38-abf0-1fc19cae9a06.png[/img]
这是因为我们使用origin方法返回时直接返回矩形内的原点引用,那么对这个引用的修改必然导致了上述的结果。出于这种原因,我们要修改origin方法,使其返回一个对象的副本,从而使得对其的修改不影响原有值:
注意这里返回时重新创建了一个对象,对于这种开销是否必要,还要根据实际情况来定。
在继承中,不能删除和减少方法,但可以通过覆盖来实现对方法的更改,还是前面的示例,定义ClassA和ClassB,代码如下:
类A接口中我们只给出变量定义(为了子类可以使用)和初始化方法,其实现代码如下:
这里我们就是实现initVar方法对变量x进行了简单的赋值。那么来看下ClassB的定义:
它继承自ClassA,并且比类A多了打印变量的方法,其实现代码如下:
代码也很简单,就是对x变量的初始化和打印,那这里也是方法覆盖的体现,那来看测试代码:
编译运行,即可得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0741/33a5371d-240d-3c78-8534-6818940e52a1.png[/img]
那么可以看到这里我们创建了类B,并且调用类B的实现代码对变量进行赋值和打印。从而实现了方法覆盖。如果我们将测试代码改写如下:
显然这里类A是没有printVar方法的,那么会得到如下错误:
[img]http://dl.iteye.com/upload/attachment/0081/0743/6e0303d3-941c-3a57-bb14-9f97bb58e4d7.png[/img]
因此我们需要修改ClassA的代码,加入printVar方法即可。我们分别创建了类A和类B的对象,它们使用各自的initVar方法后就会初始化自己的x变量,之后再使用各自的printVar方法来打印x的值。clsA和clsB按照各自所属的类选择相应的方法,这就是Objective-C中面向对象的基础。
那么如果我们将printVar方法从ClassB中删除,会是怎样的效果?因为ClassB继承自ClassA,如果ClassA中也未定义printVar方法,显然这里会出现错误。但如果ClassA中定义了printVar方法,那么ClassB就会继承这个方法。运行测试代码,也会打印出200这个值。
继承中还有抽象类的概念,如果一个类的创建只是为了更好的创建子类,那么这个类可以叫做抽象类。这样的类中可以定义实例变量和方法,但是不希望任何人从该类来创建实例,比如NSObject。在这里,只要理解抽象类的含义就可以了。
[url=http://sarin.iteye.com/blog/1820539]接下文[/url]
之前定义了矩形类Rectangle,那么我们如果要在桌面上生成这样一个矩形,就需要定位了。为了简便,我们定义桌面的左下角为直角坐标系(笛卡尔坐标系)的原点,横向向右为X轴正向,竖向向上为Y轴正向。那么我们只要确定了矩形的左下角坐标就可以得到矩形的位置了。此时我们就要引入坐标的概念,那么设计XYPoint类,代码如下:
#import <Foundation/Foundation.h>
@interface XYPoint : NSObject
@property int x,y;
-(void) setX:(int)xVal andY:(int) yVal;
@end
XYPoint.h文件定义了坐标类XYPoint的接口信息,这里面我们使用整数作为坐标,暂时不考虑小数坐标点。那么提供一个方法来设置坐标点,其实现代码为:
#import "XYPoint.h"
@implementation XYPoint
@synthesize x,y;
-(void) setX:(int)xVal andY:(int)yVal
{
x=xVal;
y=yVal;
}
@end
就是给属性x和y进行赋值,没有什么可多说的。因为我们要为矩形设置原点坐标(矩形左下角坐标),那么就需要对矩形类Rectangle进行修改,代码如下:
#import <Foundation/Foundation.h>
@class XYPoint;
@interface Rectangle : NSObject
@property int width,height;
-(int) area;
-(int) perimeter;
-(void) setWidth:(int) w andHeight:(int) h;
-(XYPoint *) origin;
-(void) setOrigin: (XYPoint *) point;
@end
这是类的接口文件,这里面我们使用了@class指令来指定XYPoint类,@class指令可以为我们指定要使用的类,而不用使用import语句,因为这里我们只需要引入XYPoint的定义而已。如果要引用类的实现部分,那么必须使用import语句,@class就不足以提供所需内容了。同时矩形类加入了两个方法,一个是设置原点origin坐标,一个是获取原点坐标,那么矩形类的实现就修改如下:
#import "Rectangle.h"
@implementation Rectangle
{
XYPoint *origin;
}
@synthesize width, height;
-(int) area
{
return width*height;
}
-(int) perimeter
{
return (width+height)*2;
}
-(void) setWidth:(int)w andHeight:(int)h
{
width=w;
height=h;
}
-(XYPoint *) origin
{
return origin;
}
-(void) setOrigin:(XYPoint *)point
{
origin=point;
}
@end
我们定义私有属性origin来表示坐标原点,提供了设置方法和获取方法,这就没什么可多说的了,最后来看看主函数,该如何使用它们:
#import "Rectangle.h"
#import "XYPoint.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Rectangle *rect=[Rectangle new];
XYPoint *point=[XYPoint new];
[point setX:10 andY:23];
[rect setWidth:10 andHeight:23];
rect.origin=point;
NSLog(@"Rectangle: width=%i, height=%i",rect.width,rect.height);
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
NSLog(@"Area = %i, Perimeter=%i",rect.area,rect.perimeter);
}
return 0;
}
主函数中需要引入两个头文件,因为使用到了它们。创建一个矩形变量和一个坐标变量,对它们赋值后,将坐标原点设置给矩形对象,那么此时矩形对象就拥有了坐标原点,之后我们打印出它们的值,编译运行后得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0731/a1619663-9a93-33b1-ae1e-5e2cc273355a.png[/img]
我们修改一下主函数,代码如下:
#import "Rectangle.h"
#import "XYPoint.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Rectangle *rect=[Rectangle new];
XYPoint *point=[XYPoint new];
[point setX:10 andY:23];
[rect setWidth:32 andHeight:36];
rect.origin=point;
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
[point setX:23 andY:10];
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
}
return 0;
}
这里只是对原点进行了二次赋值,那么编译运行后,我们得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0733/561bb550-a734-3e10-b929-2eed3fdd3b61.png[/img]
为什么会得到这样的结果?我们并没有显式的再次设置矩形的原点,只是对原点对象重新赋值后,矩形的原点也发生了相应的变化。我们来仔细看一下代码,调用setOrigin方法时,point作为参数传递给该方法,这个值是指针对象,指向了XYPoint对象的内存地址。我们使用rect.origin=point将地址赋值给矩形的原点指针上。因为这样赋值的特性,矩形中的原点和point指向的同一内存空间,那么我们修改了point的值,矩形的origin当然也会跟着改变。那么为了避免这个问题,我们修改setOrigin方法的实现,代码如下:
-(void) setOrigin:(XYPoint *) point
{
if(!origin){
origin=[[XYPoint alloc] init];
}
origin.x=point.x;
origin.y=point.y;
}
但是我们却得到了如下错误:
[img]http://dl.iteye.com/upload/attachment/0081/0735/3bb2195e-4c5a-30c0-821d-b3e17283efed.png[/img]
这是因为在Rectangle.h中我们使用@class指令来标识XYPoint,而现在需要XYPoint的细节,那么就需要修改头文件,将@class指令改为#import即可。之后修改主函数如下:
#import "Rectangle.h"
#import "XYPoint.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Rectangle *rect=[Rectangle new];
XYPoint *point=[XYPoint new];
[point setX:10 andY:23];
[rect setWidth:32 andHeight:36];
[rect setOrigin:point];
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
[point setX:23 andY:10];
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
}
return 0;
}
编译运行,得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0737/e0a17c3c-f575-3553-9024-504a729bcee8.png[/img]
这样就很合理了,原点就属于矩形自己的,称为它的一个属性了,再次修改坐标点不会对已有原点产生影响。但是问题又产生了,修改主函数如下:
#import "Rectangle.h"
#import "XYPoint.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
Rectangle *rect=[Rectangle new];
XYPoint *point=[XYPoint new];
[point setX:10 andY:23];
[rect setWidth:32 andHeight:36];
[rect setOrigin:point];
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
[point setX:23 andY:10];
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
XYPoint *origin=rect.origin;
origin.x=32;
origin.y=36;
NSLog(@"Origin at (%i, %i)",rect.origin.x,rect.origin.y);
}
return 0;
}
如果我们在这里又定义一个对象来获取矩形的原点,然后对其重新赋值,那么我们得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0739/1502da61-3b24-3a38-abf0-1fc19cae9a06.png[/img]
这是因为我们使用origin方法返回时直接返回矩形内的原点引用,那么对这个引用的修改必然导致了上述的结果。出于这种原因,我们要修改origin方法,使其返回一个对象的副本,从而使得对其的修改不影响原有值:
-(XYPoint *) origin
{
XYPoint *point=[XYPoint new];
point.x=origin.x;
point.y=origin.y;
return point;
}
注意这里返回时重新创建了一个对象,对于这种开销是否必要,还要根据实际情况来定。
在继承中,不能删除和减少方法,但可以通过覆盖来实现对方法的更改,还是前面的示例,定义ClassA和ClassB,代码如下:
#import <Foundation/Foundation.h>
@interface ClassA : NSObject
{
int x;
}
-(void) initVar;
@end
类A接口中我们只给出变量定义(为了子类可以使用)和初始化方法,其实现代码如下:
#import "ClassA.h"
@implementation ClassA
-(void) initVar
{
x=100;
}
@end
这里我们就是实现initVar方法对变量x进行了简单的赋值。那么来看下ClassB的定义:
#import "ClassA.h"
@interface ClassB : ClassA
-(void) initVar;
-(void) printVar;
@end
它继承自ClassA,并且比类A多了打印变量的方法,其实现代码如下:
#import "ClassB.h"
@implementation ClassB
-(void) initVar
{
x=200;
}
-(void) printVar
{
NSLog(@"x = %i",x);
}
@end
代码也很简单,就是对x变量的初始化和打印,那这里也是方法覆盖的体现,那来看测试代码:
#import "ClassB.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
ClassB *clsB=[ClassB new];
[clsB initVar];
[clsB printVar];
}
return 0;
}
编译运行,即可得到如下结果:
[img]http://dl.iteye.com/upload/attachment/0081/0741/33a5371d-240d-3c78-8534-6818940e52a1.png[/img]
那么可以看到这里我们创建了类B,并且调用类B的实现代码对变量进行赋值和打印。从而实现了方法覆盖。如果我们将测试代码改写如下:
#import "ClassB.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
ClassA *clsA=[ClassA new];
ClassB *clsB=[ClassB new];
[clsA initVar];
[clsA printVar];
[clsB initVar];
[clsB printVar];
}
return 0;
}
显然这里类A是没有printVar方法的,那么会得到如下错误:
[img]http://dl.iteye.com/upload/attachment/0081/0743/6e0303d3-941c-3a57-bb14-9f97bb58e4d7.png[/img]
因此我们需要修改ClassA的代码,加入printVar方法即可。我们分别创建了类A和类B的对象,它们使用各自的initVar方法后就会初始化自己的x变量,之后再使用各自的printVar方法来打印x的值。clsA和clsB按照各自所属的类选择相应的方法,这就是Objective-C中面向对象的基础。
那么如果我们将printVar方法从ClassB中删除,会是怎样的效果?因为ClassB继承自ClassA,如果ClassA中也未定义printVar方法,显然这里会出现错误。但如果ClassA中定义了printVar方法,那么ClassB就会继承这个方法。运行测试代码,也会打印出200这个值。
继承中还有抽象类的概念,如果一个类的创建只是为了更好的创建子类,那么这个类可以叫做抽象类。这样的类中可以定义实例变量和方法,但是不希望任何人从该类来创建实例,比如NSObject。在这里,只要理解抽象类的含义就可以了。
[url=http://sarin.iteye.com/blog/1820539]接下文[/url]