年尾有点忙,objctive C的学习都放下了,趁有空补下课^_^
一、间接的理解
在编程界有句话,“只有多添加一个间接层,计算机科学没有解决不了的问题”。其实间接很简单,就是说不在代码中直接使用某个值,而使用指向该值的指针。
生活中的例子:你可能不知道KFC的电话号码,但你可以通过网上查找KFC的电话号码,使用网上查找实际就是一种间接地方式。
间接在程序上有多种表现方式,就例如变量式的间接、文件式间接、参数式间接。
二、OOP世界中的间接使用
其实OOP真正的革命性就是在调用代码中使用间接。
首先来个过程式编程的例子,好让大家容易理解OOP的思想。
#import <Foundation/Foundation.h>
typedef enum{
kCircle,
kRectangle,
kOblateSpheroid
} ShapeType;
typedef enum{
kRedColor,
kGreenColor,
kBlueColor
} ShapeColor;
typedef struct{
int x, y, width, height;
} ShapeRect;
typedef struct{
ShapeType type;
ShapeColor fillColor;
ShapeRect bounds;
} Shape;
void drawShapes(Shape shapes[], int count){
int i;
for(i = 0; i < count; i++){
switch (shapes[i].type) {
case kCircle:
drawCircle(shapes[i].bounds, shapes[i].fillColor);
break;
case kRectangle:
drawRectang(shapes[i].bounds, shapes[i].fillColor);
break;
case kOblateSpheroid:
drawEgg(shapes[i].bounds, shapes[i].fillColor);
break;
default:
break;
}
}
}
void drawCircle(ShapeRect bounds, ShapeColor fillColor){
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
void drawRectang(ShapeRect bounds, ShapeColor fillColor){
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
void drawEgg(ShapeRect bounds, ShapeColor fillColor){
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
NSString *colorName(ShapeColor colorName){
switch(colorName){
case kRedColor :
return @"red";
break;
case kGreenColor :
return @"green";
break;
case kBlueColor:
return @"blue";
break;
}
return @"no clue";
}
int main(int argc, const char * argv[]){
Shape shapes[3];
ShapeRect rect0 = {0, 0, 10, 30};
shapes[0].type = kCircle;
shapes[0].fillColor = kRedColor;
shapes[0].bounds = rect0;
ShapeRect rect1 = {30, 40, 50, 600};
shapes[1].type = kCircle;
shapes[1].fillColor = kRectangle;
shapes[1].bounds = rect1;
ShapeRect rect2 = {15, 18, 37, 29};
shapes[2].type = kOblateSpheroid;
shapes[2].fillColor = kBlueColor;
shapes[2].bounds = rect2;
drawShapes(shapes, 3);
return (0);
}
上面的代码灰常简单明了,但有一个问题是程序的扩展和维护变得很困难,如果我们多加一种类型,必须在项目中至少4个不同位置修改程序才能完成该任务。
以下说的OOP完美解决了这些问题,它以数据为中心,函数为数据服务。
void drawShapes(id shapes[], int count){
int i;
for(i = 0; i < count; i++){
id shape = shapes[i];
[shape draw];
}
}
上面的代码可能很多同学都感觉很奇怪,一般来说方括号在C语言中代表数组,在Objective C中,方括号还有其他的意思:它们通知某个对象该做什么。在方括号内,第一项是对象,其余部分是你需要对象执行的操作。在本例中,我们通知名称为shape的对象执行draw操作。如果shape是圆形,我们会得到圆形;如果shape是矩形,我们会的到矩形。
在Objective-C中,通知对象执行某种操作称为“发送消息”或者“调用方法”。代码中[shape draw]表示shape对象发送draw消息。
还有一个比较奇怪的地方就是id,id是一种泛型,用于表示任何种类的对象。实际id是一个指针,指向其中的某个结构。
三、面向对象的例子
以下是CirCle接口的声明
@interface Circle : NSObject{
ShapeColor fillColor;
ShapeRect bounds;
}
- (void) setFillColor : (ShapeColor) fillColor;
- (void) setBounds : (ShapeRect) bounds;
- (void) draw;
@end
@interface”告诉编译器这是objective C的声明的接口“。
Circle对象需要的各种数据成员:
{
ShapeColor fillColor;
ShapeRect bounds;
}
- (void) setBounds: (ShapeRect) bounds;
奇葩的objective,在方法声明的前面还多加个”-“,短线后面是方法的返回类型,位于圆括号中。接着后面的你们都懂的,就是参数的声明。
Circle的实现代码:
@implementation Circle
-(void) setFillColor : (ShapeColor) c
{
fillColor =c;
}
-(void) setBounds : (ShapeRect) b
{
bounds = b;
}
-(void) draw
{
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
@end
跟声明接口一样,实现的也需要以@implementaction开头,结尾使用@end。
实际上objectvie - c中调用方法是,一个名为self的秘密隐藏参数被传递给接收对象。
以上例子中的fillColor = c,可以写成self->fillCOlor = c;
接下来最后一步实例化对象
实例化对象时,需要分配内存,然后这些内存被初始化并保存一些有用的默认值。
int main(int argc, const char * argv[]){
id shapes[3];
ShapeRect rect0 = {0, 0, 10, 30};
shapes[0] = [Circle new];
[shapes[0] setBounds : rect0];
[shapes[0] setFillColor : kRedColor];
ShapeRect rect1 = {30, 40, 50, 60};
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
[shapes[1] setFillColor: kGreenColor];
ShapeRect rect2 = {15, 19, 37, 29};
shapes[2] = [OblateShereoid new];
[shapes[2] setBounds : rect2];
[shapes[2] setFillCOlor : kBlueColor];
drawShapes(shapes, 3);
return (0);
}
看上去,oop例子中的main方法跟过程化例子的main有点相似,oop例子中含有id数组,而不是shapes数组,创建对象时都需要发送new消息。
如果我们在程序上再添加一种类型,只需实现接口就可以扩展。
@implementation Triangle
-(void) setFillColor : (ShapeColor) c
{
fillColor =c;
}
-(void) setBounds : (ShapeRect) b
{
bounds = b;
}
-(void) draw
{
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor));
}
总结:
过程化编程:函数第一,数据第二
面向对象编程:数据第一、函数第二