从C/C++到Objective-C(二)--- 面向对象
扫描二维码
随时随地手机看文章
OC和C++对C的扩展最重要的当然就是“面向对象”了,学习了C++对面向对象自然对面向对象一点也不会感到陌生了,可能还觉得有点亲切呢,陌生的语言中看到熟悉的词,你说亲不亲切啊!面向对象的几个重要概念不外乎就是类,抽象,封装,多态了, 思想都差不多的,主要就是实现的具体代码不同罢了。面向对象与面向过程不同,前者是以程序的数据为中心,函数为数据服务。
OC中有个概念叫“间接”,意思就和这个字面意思差不多,比如说基本的变量就是间接的一种实际运用,例如:int number = 8; 这里的number就代表了数字8,当然也可以给它赋不同的值,它也就代表不同的数字,这里的number就代表了你给它赋的值。“间接”所指的就是这样一种关系。我理解的是,这里的间接其实也就是抽象的一种运用罢了,很大的问题抽象出几个间接层来,问题就被分小了,当然解决起来也就容易多了。
这里给一个程序,是文件名的间接,这个还是挺好理解的。贴这段代码是想表达,OC是对C的扩展,所以C中的很多东西都是可以直接用的,当然头文件得换换了。
#importint main (int argc, const char * argv[]) { FILE *wordFile = fopen ("/tmp/words.txt", "r"); char word[100]; while (fgets(word, 100, wordFile)) { // strip off the trailing n word[strlen(word) - 1] = ' '; NSLog (@"%s is %d characters long", word, strlen(word)); } fclose (wordFile); return (0); } // main
main函数的用法也和C中是一样的,argc代表命令行中输入的参数个数,默认的是1,代表的就是程序本身的名字,如果在命令行中输入一个参数那就是argv[1]了。
下面就主要说说OC中面向对象编程的具体代码了。
先贴一段代码,主要说说与C中的两个不同点:
void drawShapes (id shapes[], int count) { int i; for (i = 0; i < count; i++) { id shape = shapes[i]; [shape draw]; } } // drawShapes
这里的id是OC中的标识符。id是一种泛型,可以用来引用任何类型的对象,我觉得有点类似C中void * 的用法,使用void*代表指向任何数据类型的指针。还有一个就是中括号[ ]的用法,[shape draw]; 这里就不在是C中用来代表数组的符号了,在OC中中括号这样的用法是用来通知某个对象该去做什么。中括号里的第一项代表的是对象,其余部分则是需要对象执行的操作。OC中通知对象执行某种操作称为发送消息,通俗点说就是调用该对象的方法。这里的[shape draw]; 表示向shape对象发送了draw消息,其实也就是调用shape对象中的draw方法。
感觉OC中比C++更加明显的区分了接口和实现这两个概念。用两个编译器指令@interface和@implementation区分了类中接口和实现。通过这两个指令分别把相关的信息传递给编译器。
首先看看接口代码:
@interface Circle : NSObject { ShapeColor fillColor; ShapeRect bounds; } - (void) setFillColor: (ShapeColor) fillColor; - (void) setBounds: (ShapeRect) bounds; - (void) draw; @end // Circle
初次看着是会觉得有些奇怪,和C++中还是有些不一样的。在OC中只要看到@符号就可以把它当成是对C语言的扩展,这里的@interface Circle告诉编译器:这是新类Circle的接口。大括号中的两行内容就是类Circle中的数据成员,这点和C++中类似的,创建出得Circle新对象都是有着两个元素构成的,ShapeColor和ShapeRect分别是枚举和结构体,最下面贴有具体的代码。这里fillColor和bounds的值称为Circle类的实例变量。
下面带短线的三行内容就是类Circle中的方法声明。其中前面的短线表明这是OC中方法的声明。这是OC中区分函数原型与方法声明的一种方式,函数原型中没有先行短线,例如上面的drawShapes()函数。第一个小括号中的void是返回类型,void表示不返回任何值。setFillColor:代表该方法的名称,注意结尾处的冒号是名称的一部分,它告诉编译器和编程人员后面会出现方法的参数。(ShapeColor)fillColor,参数的类型为ShapeColor,在小括号中指定,fillColor就是参数名了,在后面方法的具体实现中可以使用该名称来引用参数。当然你可能也注意到了后面的draw方法是不带参数的,所以也就没有加冒号:了,冒号是方法名称的重要组成部分,如果方法使用了参数,则需要加冒号,否则就不需要冒号。最后面的@end则是告诉编译器已经完成了Circle类的声明。
OC中有一种名为中缀法的语法技术,方法的名称及其参数都是合在一起的。比如:
[circle setFillColor: KRedColor]; 表示调用circle对象的setFillColor方法,并且带了KRedColor参数。
调用带两个参数的方法则为:[textThing setString:@"hello world" color:kBlueColor]; 这个理解起来就稍微有些困难了,首先同样的,textThing为对象名,那函数名呢?如果非要说个函数名的话,那就应该是setString:color:了,这里的setString: 和color:分别是参数@“hello world"和kBlueColor的名称。这就是中缀法添加的东西了。如果这个方法的返回值为void那转成oc的函数声明语法大概就是:
-(void) setString:(NSString*) strValue color:(ShapeColor) colorValue;
那转成C++中的方法声明大概就是:
void setStringcolor (NSString* strValue, ShapeColor colorValue);
这样解释后对中缀法应该就会好理解一些了。
刚才我们说了Circle类的@interface部分,主要就是用来定义类的公共接口的,接下来就说下类的@implementation实现部分了。同样先贴代码:
@implementation Circle - (void) setFillColor: (ShapeColor) c { fillColor = c; } // setFillColor - (void) setBounds: (ShapeRect) b { bounds = b; } // setBounds - (void) draw { NSLog (@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor)); } // draw @end // Circle
@implementation的作用和@interface类似,也是一个编译器指令,表明为Circle类提供实现代码。定义的方法和规则都是和C++中的类似,定义顺序不一定按照声明顺序来。这里的把setFillColor和setBounds的参数名换成了c和b,目的是为了把上面定义的实例变量和参数变量区分看来,避免覆盖掉定义的实例变量。这里和C++还是有些不同的,C++中的数据成员变量名和方法参数名一样也可以。
上列代码中的 fillColor=c; 这里有一个类似C++中this指针的参数self,所以这句代码还可以这样写 self -> fillColor = c; self和this指针一样也是隐藏的。
最后就是实例化对象了:
int main (int argc, const char * argv[]) { id shape; ShapeRect rect0 = { 0, 0, 10, 30 }; shape = [Circle new]; [shape setBounds: rect0]; [shape setFillColor: kRedColor]; }
在创建一个新对象时,向类发送new消息,也就类似C++中调用new方法,调用后就生成了一个实例对象。
下面是整个例子的详细代码:
#import// -------------------------------------------------- // constants for the different kinds of shapes and their colors typedef enum { kRedColor, kGreenColor, kBlueColor } ShapeColor; // -------------------------------------------------- // Shape bounding rectangle typedef struct { int x, y, width, height; } ShapeRect; // -------------------------------------------------- // convert from the ShapeColor enum value to a human-readable name NSString *colorName (ShapeColor color) { switch (color) { case kRedColor: return @"red"; break; case kGreenColor: return @"green"; break; case kBlueColor: return @"blue"; break; } return @"no clue"; } // colorName // -------------------------------------------------- // All about Circles @interface Circle : NSObject { ShapeColor fillColor; ShapeRect bounds; } - (void) setFillColor: (ShapeColor) fillColor; - (void) setBounds: (ShapeRect) bounds; - (void) draw; @end // Circle @implementation Circle - (void) setFillColor: (ShapeColor) c { fillColor = c; } // setFillColor - (void) setBounds: (ShapeRect) b { bounds = b; } // setBounds - (void) draw { NSLog (@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor)); } // draw @end // Circle // -------------------------------------------------- // All about Rectangles @interface Rectangle : NSObject { ShapeColor fillColor; ShapeRect bounds; } - (void) setFillColor: (ShapeColor) fillColor; - (void) setBounds: (ShapeRect) bounds; - (void) draw; @end // Rectangle @implementation Rectangle - (void) setFillColor: (ShapeColor) c { fillColor = c; } // setFillColor - (void) setBounds: (ShapeRect) b { bounds = b; } // setBounds - (void) draw { NSLog (@"drawing a rectangle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor)); } // draw @end // Rectangle // -------------------------------------------------- // All about OblateSphereoids @interface OblateSphereoid : NSObject { ShapeColor fillColor; ShapeRect bounds; } - (void) setFillColor: (ShapeColor) fillColor; - (void) setBounds: (ShapeRect) bounds; - (void) draw; @end // OblateSphereoid @implementation OblateSphereoid - (void) setFillColor: (ShapeColor) c { fillColor = c; } // setFillColor - (void) setBounds: (ShapeRect) b { bounds = b; } // setBounds - (void) draw { NSLog (@"drawing an egg at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.width, bounds.height, colorName(fillColor)); } // draw @end // OblateSphereoid // -------------------------------------------------- // Draw the shapes void drawShapes (id shapes[], int count) { int i; for (i = 0; i < count; i++) { id shape = shapes[i]; [shape draw]; } } // drawShapes // -------------------------------------------------- // The main function. Make the shapes and draw them 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] = [OblateSphereoid new]; [shapes[2] setBounds: rect2]; [shapes[2] setFillColor: kBlueColor]; drawShapes (shapes, 3); return (0); } // main
参考书籍:《Objective-C 基础教程(第二版)》