3 面向对象编程的基础知识

OOP历史:演变自20世纪60年代的Simula、70年代的SmallTalk、80年代的Clascal以及其他相关语言。C++JavaPythonObjective-C等现代编程语言都从这个早期的语言中获得了灵感。

3.1 间接

只要在多加一层间接,计算机科学中就没有解决不了的问题

说明:指的就是面向对象的多态性
原理:在程序运行期间,通过指针间接获取某个值,而不是直接获取(指针指向的变化对程序员不可见)。

3.1.1 变量与间接

说明:基本变量就是间接思想的一种实际应用。

1
2
3
4
5
6
7
8
9
10
11
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
// 通过修改 count 间接控制后面循环的次数,本身就蕴含着间接的思想
int count = 5;
NSLog(@"The numbers from 1 to %d:", count);
for (int i = 1; i <= count; i++) {
NSLog(@"%d\n", i);
}
return 0;
}

3.1.2 使用文件名的间接

说明:程序中使用文件(而不是写死在代码中)也是间接思想的运用。
技巧:xcode中运行程序时提供文件路径的方式

  1. 打开设置面板:Product(菜单)-> Scheme -> Edit Scheme
  2. 在面板中添加参数:Arguments -> Arguments Passed On Launch—>点击+添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
// 命令中没有提供文件名,则退出程序
if (argc == 1) {
NSLog(@"you need to provide a file name");
return (1);
}

// 1. 打开文件
FILE *wordFile = fopen(argv[1], "r");

// 2. 访问文件
char word[100];

// 一行一行地读区
while (fgets(word, 100, wordFile)) {
// 将每行最后的换行符替换为字符串结束符(从而不会被后面的 strlen 计入字符串的长度)
word[strlen(word) - 1] = '\0';
NSLog(@"%s is %lu characters long", word, strlen(word));
}
// 3. 关闭文件
fclose(wordFile);
return 0;
}

3.2 在面向对象编程中使用间接

说明:OOP的核心在于使用间接来调用代码(而不是直接调用函数)

3.2.1 过程式编程

说明:在过程式编程中,数据通常保存在简单的数据结构中。要花时间连接数据和用来处理数据的函数。
语言举例:BASICCTclPerl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#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;

/**
* 将传入的颜色值转换为字符串描述
* @param {ShapeColor} colorName 图形颜色
* @return {NSString} 枚举类型的颜色对应的字符串
*/

NSString *colorName (ShapeColor colorName) {
switch (colorName) {
case kRedColor:
return @"red";
break;
case kGreenColor:
return @"green";
case kBlueColor:
return @"blue";
default:
break;
}
}

/**
* 绘制圆形
* @param {ShapeRect} bounds 绘制区域
* @param {ShapeColor} fillColor 图形填充色
*/

void drawCircle (ShapeRect bounds, ShapeColor fillColor) {
NSLog(@"drawing a circle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.height, bounds.width, colorName(fillColor));
}

/**
* 绘制矩形
* @param {ShapeRect} bounds 绘制区域
* @param {ShapeColor} fillColor 图形填充色
*/

void drawRectangle (ShapeRect bounds, ShapeColor fillColor) {
NSLog(@"drawing a rectangle at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.height, bounds.width, colorName(fillColor));}

/**
* 绘制椭圆形
* @param {ShapeRect} bounds 绘制区域
* @param {ShapeColor} fillColor 图形填充色
*/

void drawEgg (ShapeRect bounds, ShapeColor fillColor) {
NSLog(@"drawing a egg at (%d %d %d %d) in %@", bounds.x, bounds.y, bounds.height, bounds.width, colorName(fillColor));
}

/**
* 绘制图形
* @param {Shape[]} shapes 要绘制的一组图形
* @param {int} count 绘制数组中的前count个图形
*/

void drawShapes (Shape shapes[], int count) {
for (int i = 0; i < count; 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;
}
}
}

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;
}
1
2
3
4
2016-01-02 00:50:43.911 3.2.1-procedureOriented[834:55458] drawing a circle at (0 0 30 10) in red
2016-01-02 00:50:43.912 3.2.1-procedureOriented[834:55458] drawing a rectangle at (30 40 60 50) in green
2016-01-02 00:50:43.912 3.2.1-procedureOriented[834:55458] drawing a egg at (15 18 29 37) in blue
Program ended with exit code: 0

3.2.2 实现面向对象编程

说明:过程式编程建立在函数之上,数据为函数服务,而面向对象编程则以程序的数据为中心,函数为数据服务。
原理:数据通过间接方式引用相应代码对数据进行操作(数据能够知道如何查找相应的函数)。

id(标识符)

说明:identifier,是一种泛型,可以用来定义引用任何类型对象的变量。

发送消息

说明:通知对象执行某种操作,也叫做调用方法

说明:不同于其他OOP系统,在O-C中,如果在运行时改变某个类,则该类的所有对象会自动继承这些变化

3.3 有关术语

说明:有关面向对象的术语

术语 说明 语法 备注
类(class) 一种表示对象类型的结构体 类名首字母大写 对象通过它的类来获取自身的各种信息
对象或实例(object) 一种包含值和指向其类的隐藏指针的结构体 指向对象的变量通常首字母不要大些
消息(message) 对象可以执行的操作,用于通知对象做什么
方法(method) 响应消息运行的代码 根据对象的类,消息可以调用不同的方法
方法调度(method dispatcher) 用于推测执行什么方法以响应某个特定的消息
接口(interface) 类为对象提供的特性描述
实现(implementation) 使接口能正常工作的代码

3.4 Object-C语言中的OOP

3.4.1 @interface部分

说明:定义类的公共接口,真正运行的代码位于@implementation中。
用途:在特定类的对象被创建时,为编译器提供有关该类的信息,尤其是对象的数据成员及其提供的功能。
组成:语法上包含以下几个部分

  • 继承其它interface
  • 实现protocol
  • 实例变量
  • 方法声明
1
2
3
4
5
6
7
@interface 类名: 父类名 {
// 实例属性
属性类型 属性名;
}
// 方法声明
- (返回值类型) 方法名: (参数1类型)参数1名称, (参数2类型)参数2名称;
@end// 类名(便于代码阅读)

注意:在声明方法时,如果方法有参数,则需要冒号,否则不需要。

1
2
3
4
5
6
7
8
9
10
11
12
// Circle类
@interface Circle: NSObject {
// 实例变量
@private
ShapeColor fillColor;
ShapeRect bounds;
}
// 方法声明
- (void) setFillCollor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end// Circle

3.4.2 @implementation部分

说明:@implementation是一个编译器指令,表明将为某个类提供代码

  • 方法的定义不必按照在@interface指令中的顺序出现
  • 可以定义没有在@interface中声明过的方法
  • 同一个类中,方法的参数名不可以和实例属性名相同,否则方法被调用时会覆盖实例属性的值

扩展:

  • 实例变量名:如果实例变量取一个和方法名相同的名字,Cocoa还可以发挥出某些神奇的威力
  • 运行时:O-C运行时是指用户应用程序时,支持这些应用程序的代码块
  • self:类的方法被调用时,一个名为self的秘密隐藏参数将被传递给接受对象,而这个参数引用的就是该接收对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@implementation Circle

/**
* 设置图形填充色
* @param {ShapeColor} c 颜色
*/

- (void) setFillCollor:(ShapeColor)c {
fillColor = c;
}

/**
* 设置绘图区域
* @param {ShapeRect} b 绘图区域
*/

- (void) setBounds:(ShapeRect)b {
bounds = b;
}
@end

3.4.3 实例化对象

说明:实例化对象时,需要分配内存,然后将这些内存初始化并保存为有用的默认值。
语法:向相应的(或已经存在的对象)发送new消息,该类接收并处理完new消息后,我们就会得到一个可以使用的新对象实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#import <Foundation/Foundation.h>
// --------------------------------------------------
// 图形类型
typedef enum {
kCircle,
kRectangle,
kEgg
} ShapeType;

// --------------------------------------------------
// 图形颜色
typedef enum {
kRedColor,
kGreenColor,
kBlueColor
} ShapeColor;

// --------------------------------------------------
// 绘图区域
typedef struct {
int x, y, width, height;// 坐标和边长
} ShapeRect;

// --------------------------------------------------
/**
* 将传入的颜色值转换为字符串描述
* @param {ShapeColor} colorName 图形颜色
* @return {NSString} 枚举类型的颜色对应的字符串
*/

NSString *colorName (ShapeColor colorName) {
switch (colorName) {
case kRedColor:
return @"red";
break;
case kGreenColor:
return @"green";
case kBlueColor:
return @"blue";
default:
break;
}
}

// --------------------------------------------------
// Circle类
@interface Circle: NSObject {
// 实例变量
@private
ShapeColor fillColor;
ShapeRect bounds;
}
// 方法声明
- (void) setFillCollor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end// Circle


// Circle类的实现
@implementation Circle

/**
* 设置图形填充色
* @param {ShapeColor} c 颜色
*/

- (void) setFillCollor:(ShapeColor)c {
fillColor = c;
}

/**
* 设置绘图区域
* @param {ShapeRect} b 绘图区域
*/

- (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) setFillCollor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end// Rectangle


// Rectangle类的实现
@implementation Rectangle

/**
* 设置图形填充色
* @param {ShapeColor} c 颜色
*/

- (void) setFillCollor:(ShapeColor)c {
fillColor = c;
}

/**
* 设置绘图区域
* @param {ShapeRect} b 绘图区域
*/

- (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) setFillCollor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end// Egg


// Egg类的实现
@implementation Egg

/**
* 设置图形填充色
* @param {ShapeColor} c 颜色
*/

- (void) setFillCollor:(ShapeColor)c {
fillColor = c;
}

/**
* 设置绘图区域
* @param {ShapeRect} b 绘图区域
*/

- (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

// --------------------------------------------------
/**
* 绘制图形
* @param {id[]} shapes 要绘制的一组图形
* @param {int} count 绘制数组中的前count个图形
*/

void drawShapes (id shapes[], int count) {
for (int i = 0; i < count; i++) {
id shape = shapes[i];
[shape draw];
}
}

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] setFillCollor:kRedColor];

// 绿色矩形
ShapeRect rect1 = {30, 40, 50, 60};
shapes[1] = [Rectangle new];
[shapes[1] setBounds: rect1];
[shapes[1] setFillCollor:kGreenColor];

// 蓝色椭圆
ShapeRect rect2 = {15, 18, 37, 29};
shapes[2] = [Egg new];
[shapes[2] setBounds: rect2];
[shapes[2] setFillCollor:kBlueColor];

drawShapes(shapes, 3);
return 0;
}

3.4.4 扩展 Shapes-Object 程序

说明:软件实体应该对扩展开放,而对修改关闭

3.5 小结