6 源文件组织

说明:将程序拆分为多个小文件有助于更快地找到重要的代码。

6.1 拆分接口与实现

说明:接口放在.h文件,实现放在.m文件。

文件 存放代码
头文件(.h) 类的@interface指令、公共struct定义、enum常量、#definesextern全局变量等
和类同名的.m(或.mm)文件 类的@implementation指令、全局变量的定义、私有struct等

注意:如果用.mm做文件扩展名,编译器就会认为你是用Objective-C++(同时使用C++Objective-C)编写的代码

在Xcode中创建新文件

说明:创建包含类的文件

  1. File(菜单)-> New -> File…
  2. 选择模版:ios -> Source -> Cocoa Class
  3. 填写类名
  4. 选择父类(默认NSObject)
  5. 选择群组和目标
  6. 选择存储位置并保存
术语 说明 备注
群组(Group) 文件都放在群组内的文件夹中,将代码分组存放可以帮助组织项目中的源文件 1. 设置群组时,Xcode并不会在硬盘上移动文件或者创建目录,群组关系仅仅是Xcode负责管理的一项奇妙的功能;2. 可以设置群组指向文件系统中某个特定的目录,Xcode会新建的文件放入该目录中
目标(Target) 复杂的项目可以拥有多个目标,它们源文件的配置各不相同,构建规则也不同

6.2 拆分 Car 程序

说明:每个类都由头文件(.h)和实现文件(.m)组成,入口在main.m

  • 头文件(.h文件):通过import引入模块的过程放在头文件
  • 实现文件(.m文件):文件中需要引入相应的头文件(.h)文件

技巧:复合的程序文件中使用了其它类,如果只是通过指针引用了相应的类实例,可以用@class声明引用的类而不需要import,从而提升重新编译时的性能。

导入文件:import文件有两种方式

导入 说明
#import "文件" 导入项目本地的头文件
#import <文件> 导入系统文件(只读)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.
├── 6.2\ CarPaers-Split# 存放代码的群组
│ ├── AllWeatherRadial.h
│ ├── AllWeatherRadial.m
│ ├── Car.h
│ ├── Car.m
│ ├── Engine.h
│ ├── Engine.m
│ ├── Slant6.h
│ ├── Slant6.m
│ ├── Tire.h
│ ├── Tire.m
│ └── main.m# 入口文件
└── 6.2\ CarPaers-Split.xcodeproj
├── project.pbxproj
└── xcuserdata
└── tonyearth.xcuserdatad

6.3 使用快文件依赖关系

说明:依赖关系可以存在于两个或多个文件之间,并且具有传递性,如果其中一个文件发生变化,其它文件都会被重新编译。

6.3.1 重新编译须知

说明:文件导入过于混乱会延长编译时间,也会导致不必要的重复编译,可以通过@class减少必需导入的头文件的数量,从而可以缩短编译时间。

@class

说明:创建一个前向引导,用在程序不需要知道类的所有信息的场景(比如创建了相应的变量,但没有向变量发送消息)。
技巧:如果试图通过#import语句让两个类相互引用,那么就会出现编译错误,可以用@class替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import <Cocoa/Cocoa.h>

@class Tire;
@class Engine;

@interface Car : NSObject

- (void) setEngine: (Engine *) newEngine;

- (Engine *) engine;


- (void) setTire: (Tire *) tire
atIndex: (int) index;

- (Tire *) tireAtIndex: (int) index;

- (void) print;

@end // Car

6.3.2 让汽车跑一会儿

Car.m

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
#import "Car.h"
#import "Engine.h"
#import "Tire.h"

@implementation Car
{
Tire *tires[4];
Engine *engine;
}

- (id) init
{
if (self = [super init]) {
engine = [Engine new];

tires[0] = [Tire new];
tires[1] = [Tire new];
tires[2] = [Tire new];
tires[3] = [Tire new];
}

return (self);

} // init


- (Engine *) engine
{
return (engine);
} // engine


- (void) setEngine: (Engine *) newEngine
{
engine = newEngine;
} // setEngine


- (void) setTire: (Tire *) tire
atIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in setTire:atIndex:",
index);
exit (1);
}

tires[index] = tire;

} // setTire:atIndex:


- (Tire *) tireAtIndex: (int) index
{
if (index < 0 || index > 3) {
NSLog (@"bad index (%d) in tireAtIndex:",
index);
exit (1);
}

return (tires[index]);

} // tireAtIndex:



- (void) print
{
NSLog (@"%@", engine);

NSLog (@"%@", tires[0]);
NSLog (@"%@", tires[1]);
NSLog (@"%@", tires[2]);
NSLog (@"%@", tires[3]);

} // print

@end // Car

6.3.3 导入和继承

说明:如果当前的文件头文件中的类会继承要引入的类,则不能通过@class引入,因为编译器需要知道父类中实例变量的完整信息。

Slant6.h

1
2
3
4
5
#import "Engine.h"

@interface Slant6 : Engine

@end

6.4 小结