第三章、面向对象编程的基础知识

1.间接

  • 1.1变量与间接
    基本变量就是间接的一种实际使用
    为避免在代码中修改数值,可以将数字放入某个变量中,添加一层间接
#import <Foundation/Foundation.h>
int main (int argc,const char *argv[]){
       NSlog(@"The numbers from 1 to 5:");
       for (int i = 1; i <= 5; i++) {
          NSLog(@"%d\n",i);
       }
       return 0;
}
//使用间接
#import <Foundation/Foundation.h>
int main (int argc,const char *argv[]){
       int count = 5;
       NSlog(@"The numbers from 1 to %d:",count);
       for (int i = 1; i <= count; i++) {
          NSLog(@"%d\n",i);
       }
       return 0;
}
  • 1.2使用文件名的间接
    文件是间接的另一个示例
    为避免在代码中修改数据,可以将数字放入某个文件中,添加一层间接
#import <Foundation/Foundation.h>
int main (int argc,const char *argv[]){
       const char *words[4] = {"aardvark","abacus","allude","zygote"};
       int wordCount = 4;
       for (int i = 0; i < wordCount; i++) {
          //strlen相当于sizeof
          NSLog(@"%s is %lu character long",words[i],strlen(words[i]));
       }
       return 0;
}
//当需要换另一组单词,很麻烦
//另一个方法就是将所有的名字都移到代码之外的某个文件中,每行一个文字,这就是间接
#import <Foundation/Foundation.h>
int main (int argc,const char *argv[]){
      FILE *wordFile = fopen ("/tmp/words.txt","r");//fopen()函数打开word.txt文件
      char word[100];
      //fgets()从文件中读取一行文本并将其放入字符数组word中
     //fgets()调用会保留每行之间用来断行的换行符,但我们不需要它,因为如果留下来,换行符就会被计为单词中的一个字符。为了解决这个问题,我们将换行符换为\0,这表示字符串的结束。
      while (fgets(word, 100, wordFile)){
          word[strlen(word) - 1] = '\0';
              NSLog(@"%s is %lu characters long",word,strlen(word));
      }
      fclose (wordFile);
      return 0;
}

  • 传递隐藏的参数是间接的又一个示例

2.在面向对象过程中使用间接

  • 2.1过程式编程
    使用过程式编程,要花时间连接数据和用来处理该数据的函数
    数据类型对应正确的函数易出错
    程序的扩展和维护很困难
//Shape程序
//绘制出一个红色的圆形,绿色的矩形,蓝色的椭圆形
#import <Foundation/Foundation.h>
//通过枚举指定几种可以绘制的不同形状
typedef enum {
    kCircle,
    kRectangle,
    kEgg,
}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);
//输出矩形区域信息和传递给他的颜色
void drawCircle(ShapeRect bounds,ShapeColor fillColor);
void drawRectangle(ShapeRect bounds,ShapeColor fillColor);
void drawEgg(ShapeRect bounds,ShapeColor fillColor);
//传入颜色值
NSString *colorName (ShapeColor colorName);
//主函数
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,60};
    shapes[1].type = kRectangle;
    shapes[1].fillColor = kGreenColor;
    shapes[1].bounds = rect1;
    //设计蓝色的椭圆形
    ShapeRect rect2 = {15,18,37,29};
    shapes[2].type = kEgg;
    shapes[2].fillColor = kBlueColor;
    shapes[2].bounds = rect2;
    //调用函数绘制形状
    drawShapes (shapes,3);
    return 0;
}
void drawShapes (Shape shapes[], int count) {
    for (int i = 0; i < 3; i++) {
        switch (shapes[i].type) {
            case kCircle:
                drawCircle (shapes[i].bounds,shapes[i].fillColor);
                break;
            case kRectangle:
                drawRectangle(shapes[i].bounds,shapes[i].fillColor);
                break;
            case kEgg:
                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 drawRectangle(ShapeRect bounds,ShapeColor fillColor){
    NSLog(@"drawing a rectangle 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 egg 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;
        default:
            break;
    }
}
//运行结果
2018-10-02 14:33:03.021349-0700 图形[673:20069] drawing a circle at (0,0,10,30) in red
2018-10-02 14:33:03.021547-0700 图形[673:20069] drawing a rectangle at (30,40,50,60) in green
2018-10-02 14:33:03.021628-0700 图形[673:20069] drawing a egg at (15,18,37,29) in blue
Program ended with exit code: 0
  • 2.2实现面向对象编程
    在oop中,数据通过间接方式引用代码,代码可以对数据进行操作,要求这个数据绘制自身
    代码下一篇文章详细解说
//使用oop的方式写Shape程序
#import <Foundation/Foundation.h>
//通过枚举指定几种可以绘制的不同形状
typedef enum {
    kCircle,
    kRectangle,
    kEgg,
}ShapeType;
//通过枚举指定绘制形状时可用的几种颜色
typedef enum {
    kRedColor,
    kGreenColor,
    kBlueColor,
} ShapeColor;
//使用一个结构体来描述一个矩形,该矩形指定屏幕上的绘图区域
typedef struct {
    int x,y,width,height;
}ShapeRect;
//声明绘制函数
void drawShapes (id shapes[], int count);
//输出矩形区域信息和传递给他的颜色
NSString *colorName (ShapeColor colorName);
//Circle类接口
@interface Circle : NSObject {
@private
    ShapeColor fillColor;
    ShapeRect bounds;
}
- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;
- (void) draw;
@end
//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
//Rectangle类接口
@interface Rectangle: NSObject {
@private
    ShapeColor fillColor;
    ShapeRect bounds;
}
- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;
- (void) draw;
@end
//Rectangle类实现
@implementation Rectangle
-(void)setFillColor:(ShapeColor)c{
    fillColor = c;
}
-(void)setBounds:(ShapeRect)b{
    bounds = b;
}
-(void)draw{
    NSLog(@"drawing a Rectangle at (%d,%d,%d,%d) in %@",bounds.x,bounds.y,bounds.width,bounds.height,colorName(fillColor));
}
@end
//Egg类接口
@interface Egg : NSObject {
@private
    ShapeColor fillColor;
    ShapeRect bounds;
}
- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;
- (void) draw;
@end
//Egg类实现
@implementation Egg
-(void)setFillColor:(ShapeColor)c{
    fillColor = c;
}
-(void)setBounds:(ShapeRect)b{
    bounds = b;
}
-(void)draw{
    NSLog(@"drawing a Egg at (%d,%d,%d,%d) in %@",bounds.x,bounds.y,bounds.width,bounds.height,colorName(fillColor));
}
@end
//主函数
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,18,37,29};
    shapes[2] = [Egg new];
    [shapes[2] setBounds:rect2];
    [shapes[2] setFillColor:kBlueColor];
    //调用函数绘制形状
    drawShapes (shapes,3);
    return 0;
}
//绘制函数的实现
void drawShapes (id shapes[], int count) {
    for (int i = 0; i < count; i++) {
        id shape = shapes[i];
        [shape draw];
    }
}
//返回颜色函数的实现
NSString *colorName (ShapeColor colorName){
    switch (colorName) {
        case kRedColor:
            return @"red";
            break;
        case kGreenColor:
            return @"green";
            break;
        case kBlueColor:
            return @"blue";
            break;
        default:
            break;
    }
}
//运行结果
2018-10-02 16:49:29.290177-0700 图像[504:7489] drawing a Circle at (0,0,10,30) in red
2018-10-02 16:49:29.290497-0700 图像[504:7489] drawing a Rectangle at (30,40,50,60) in green
2018-10-02 16:49:29.290509-0700 图像[504:7489] drawing a Egg at (15,18,37,29) in blue
Program ended with exit code: 0

可能的报错以及处理方法

处理方法

当需要绘制新的图形时,只需要创建一个新的类,避免了过程式编程的麻烦

3.有关术语

  • 类:表示对象类型的结构体,对象通过它的类来获取自身的各种信息,建议首字母大写
  • 对象:是一种包含值和指向其类的隐藏指针的结构体,首字母通常不需要大写
  • 实例:对象的另一种称呼
  • 消息:是对象可以执行的操作,用于通知对象去做什么
  • 方法:为响应消息而运行的代码
  • 方法调度:OC的一种机制,用于推测执行什么方法以响应某个特定的消息
  • 接口:类为对象提供的特性描述
  • 实现:使接口能正常实现的代码

4.Objective-C语言中的oop

  • 4.1.@interface部分
    用于定义类的公共接口
    创建特定类的对象之前,OC编译器需要一些有关该类的信息,尤其是对象的数据成员及其提供的功能
@interface Circle : NSObject {//@interface Circle告诉编译器这是新类的接口
@private//Circle类对象需要的各种数据成员
    ShapeColor fillColor;
    ShapeRect bounds;
}
//方法声明
- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;
- (void) draw;
@end//告诉编译器,完成了Circle类的声明

对象方法:-(返回值)方法名:(参数类型)参数
对于冒号,方法使用参数才需要冒号,否则不需要
方法调用:[对象 方法:实参];

  • 4.2.@implementation部分
    真正使对象能够运行的代码
@implementation Circle//@implementation是一个编译器指令,表明你将为某个类提供代码
//方法的实现
-(void)setFillColor:(ShapeColor)c{//参数不能再用fillColor,不然我们会覆盖fillColor实例变量,并且编译器会生成警告
    fillColor = c;
}
-(void)setBounds:(ShapeRect)b{//参数不能再用bounds,不然我们会覆盖bounds实例变量,并且编译器会生成警告
    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

在实际生活中,我们必须区分参数名称和实例变量的名称

  • 4.3.实例化对象
    创建一个新的对象,需要向相应的类发送new消息
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,18,37,29};
    shapes[2] = [Egg new];//创建对象
    [shapes[2] setBounds:rect2];
    [shapes[2] setFillColor:kBlueColor];
    //调用函数绘制形状
    drawShapes (shapes,3);
    return 0;
}

id:表示的是标识符,是一种泛型,可以引用任何类型的对象,对象是一种包含代码的struct结构体,因此id实际上是一个指向结构体的指针

  • 扩展OC程序
    添加绘制三角形的功能
@interface Triangle : NSObject {//@interface Triangle告诉编译器这是新类的接口
@private//Triangle类对象需要的各种数据成员
    ShapeColor fillColor;
    ShapeRect bounds;
}
//方法声明
- (void) setFillColor:(ShapeColor) fillColor;
- (void) setBounds:(ShapeRect) bounds;
- (void) draw;
@end//告诉编译器,完成了Circle类的声明
@implementation Triangle//@implementation是一个编译器指令,表明你将为某个类提供代码
//方法的实现
-(void)setFillColor:(ShapeColor)c{//参数不能再用fillColor,不然我们会覆盖fillColor实例变量,并且编译器会生成警告
    fillColor = c;
}
-(void)setBounds:(ShapeRect)b{//参数不能再用bounds,不然我们会覆盖bounds实例变量,并且编译器会生成警告
    bounds = b;
}
-(void)draw{
    NSLog(@"drawing a Triangle at (%d,%d,%d,%d) in %@",bounds.x,bounds.y,bounds.width,bounds.height,colorName(fillColor));
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容