与C 和C++ 一样,Objective-C 也使用“头文件”(header file) 与“实现文件”(implementation file)来区隔代码。用Objective-C 语言编写“类”(class)的标准方式为:以类名做文件名称,分别创建两个文件,头文件后缀用.h,实现文件后缀用.m。

创建好一个类之后,其代码看上去例如以下所看到的:

// EOCPerson.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@end
// EOCPerson.m
#import "EOCPerson.h"
@implementation EOCPerson
// Implementation of methods
@end

用Objective-C 语言编写不论什么类差点儿都须要引入Foundation.h。假设不在该类本身引入这个文件的话。那么就要引入与其超类所属框架相相应的“基本头文件”(base header file)。

比如。在创建iOS 应用程序时,一般会继承UIViewController 类。

而这些子类的头文件须要引入UIKit.h。

如今看来,EOCPerson 类还好。其头文件引入了整个Foundation 框架。只是这并没有问题。

假设此类继承自Foundation 框架中的某个类,那么EOCPerson 类的使用者(consumer)

可能会用到其基类中的很多内容。继承自UIViewController 的那些类也是如此,其使用者可能会用到UIKit 中的大部分内容。

过段时间, 你可能又创建了一个名为EOCEmployer 的新类。 然后可能认为每一个EOCPerson 实例都应该有一个EOCEmployer。于是。直接为其增加一项属性:

// EOCPerson.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end

然而这么做有个问题,就是在编译引入了EOCPerson.h 的文件时,EOCEmployer 类并不可见。不便强迫开发人员在引入EOCPerson.h 时必须一并引入EOCEmployer.h。所以,常见的

办法是在EOCPerson.h 中增加以下这行:

#import "EOCEmployer.h"

这样的办法可行,可是不够优雅。在编译一个使用了EOCPerson 类的文件时,不须要知道

EOCEmployer 类的所有细节,仅仅须要知道有一个类名叫EOCEmployer 就好。所幸有个办法

能把这一情况告诉编译器:

@class EOCEmployer;

这叫做“向前声明”(forward declaring)该类。如今EOCPerson 的头文件变成了这样:

// EOCPerson.h
#import <Foundation/Foundation.h>
@class EOCEmployer;
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) EOCEmployer *employer;
@end

EOCPerson 类的实现文件则需引入EOCEmployer 类的头文件。由于若要使用后者,则

必须知道其所有接口细节。于是,实现文件就是:

// EOCPerson.m
#import "EOCPerson.h"
#import "EOCEmployer.h"
@implementation EOCPerson
// Implementation of methods
@end

将引入头文件的时机尽量延后,仅仅在确有须要时才引入。这样就能够减少类的使用者所需引入的头文件数量。

假设本例把EOCEmployer.h 引入到EOCPerson.h,那么仅仅要引入EOCPerson.h,就会一并引入EOCEmployer.h 的所有内容。此过程若持续下去,则要引入许

多根本用不到的内容,这当然会增加编译时间。

向前声明也攻克了两个类互相引用的问题。

假设要为EOCEmployer 类增加新增及删除雇员的方法,那么其头文件里会增加下述定义:



- (void)addEmployee:(EOCPerson*)person;

- (void)removeEmployee:(EOCPerson*)person;

此时, 若要编译EOCEmployer, 则编译器必须知道EOCPerson 这个类, 而要编译EOCPerson,则又必须知道EOCEmployer。

假设在各自头文件里引入对方的头文件,则会导致“循环引用”(chicken-and-egg situation)。

当解析当中一个头文件时,编译器会发现它引入

了还有一个头文件,而那个头文件又回过头来引用第一个头文件。

使用#import 而非#include指令尽管不会导致死循环,但却这意味着两个类里有一个无法被正确编译。

假设不信的话。读者能够自己试试。

可是有时候必须要在头文件里引入其它头文件。假设你写的类继承自某个超类。则必须引入定义那个超类的头文件。

同理,假设要声明你写的类遵从某个协议(protocol),那么该协议必须有完整定义,且不能使用向前声明。向前声明仅仅能告诉编译器有某个协议。而此时

编译器却要知道该协议中定义的方法。

比如,要从图形类中继承一个矩形类,且令其遵循绘制协议:

// EOCRectangle.h
#import "EOCShape.h"
#import "EOCDrawable.h"
@interface EOCRectangle : EOCShape<EOCDrawable>
@property (nonatomic, assign) float width;
@property (nonatomic, assign) float height;
@end

第二条#import是难免的。鉴于此,最好是把协议单独放在一个头文件里。

要是把EOCDrawable 协议放在了某个大的头文件里,那么仅仅要引入此协议,就必然会引入那个头文

件中的所有内容,如此一来,就像上面说的那样,会产生相互依赖问题,并且还会增加编译

时间。

然而有些协议。比如“托付协议”(delegate protocol),就不用单独写一个

头文件了。在那种情况下,协议仅仅有与接受协议托付的类放在一起定义才有意义。此时最好

能在实现文件里声明此类实现了该托付协议,并把这段实现代码放在“ class-continuation 分

类”(class-continuation category)里。这样的话。仅仅要在实现文件里引入包括

托付协议的头文件就可以,而不需将其放在公共头文件(public header file)里。每次在头文件里引入其它头文件之前,都要先问问自己这样做是否确有必要。

假设能够用向前声明代替引入,那么就不要引入。

若由于要实现属性、实例变量或者要遵循协议而必须引入头文件,则应尽量将其移至“ class-continuation 分类”中。

这样做不仅能够缩减编译时间,并且还能减少彼此依赖程度。若是依赖关系过于复杂。则会给维护带来麻烦,并且,假设仅仅想把代码的某个部分开放为公共API 的话,太复杂的依赖关系也会出问题。

要点 :

  • 除非确有必要,否则不要引入头文件。一般来说,应在某个类的头文件里使用向前声

    明来提及别的类,并在实现文件里引入那些类的头文件。这样做能够尽量减少类之间

    的耦合(coupling)。
  • 有时无法使用向前声明,比方要声明某个类遵循一项协议。这样的情况下,尽量把“该类遵循某协议”的这条声明移至“ class-continuation 分类”中。

    假设不行的话,就把协议单独放在一个头文件里,然后将其引入。

在类的头文件里尽量少引入其它头文件 &lt;&lt;Effective Objective-C&gt;&gt;的更多相关文章

  1. Effective Objective-C 2.0 — 第二条:类的头文件中尽量少引入其他头文件

    第二条:类的头文件中尽量少引入其他头文件 使用向前声明(forward declaring) @class EOCEmployer 1, 将引入头文件的实际尽量延后,只在确有需要时才引入,这样就可以减 ...

  2. 使用Java中的IO流,把A文件里的内容输入到B文件中

    我们先创建两个文本文件,out.txt和in.txt,在out.txt中输入"Hello World",然后使用FileInputStream把字符串读取出来,再使用FileOut ...

  3. 读写文件:每次读入大文件里的一行、读写.CSV文件

    读文件: 传统的读法.所有读出,按行处理: fp=open("./ps.txt", "r"); alllines=fp.readlines(); fp.clos ...

  4. .vue文件里引用单独样式和js文件

    style只能引一个,script可以引多个

  5. 引入其他服务器的JS文件,同时也引入本地JS文件 报错时:

    控制台报错: Uncaught ReferenceError: define is not defined at core.js:5

  6. 关于新版vue-cli安装json-server在build文件里没生成出dev-server文件

    今天在安装json-server时遇到一个问题,build文件里并没有生成dev-server.js文件, 开始是怀疑配置有问题,或者安装不正确,然后重新安装了两三次,还是这样,郁闷.. 通过查询资料 ...

  7. Github使用.gitignore文件忽略不必要上传的文件 (转)

    原文地址: https://blog.csdn.net/gjy211/article/details/51607347 常用编程语言及各种框架平台下的通用   .gitignore   文件 http ...

  8. 头文件里面的ifndef /define/endif的作用

    c,c++里面,头文件里面的ifndef /define/endif的作用 今天和宿舍同学讨论一个小程序,发现有点地方不大懂······ 是关于头文件里面的一些地方: 例如:要编写头文件test.h ...

  9. 《Java虚拟机原理图解》1.3、class文件里的訪问标志、类索引、父类索引、接口索引集合

    讲完了class文件里的常量池,我们就相当于克服了class文件里最麻烦的模块了.如今,我们来看一下class文件里紧接着常量池后面的几个东西:訪问标志.类索引.父类索引.接口索引集合. 1. 訪问标 ...

随机推荐

  1. 2019天梯赛练习题(L2专项练习)

    7-2 列出连通集 (25 分) 给定一个有N个顶点和E条边的无向图,请用DFS和BFS分别列出其所有的连通集.假设顶点从0到N−1编号.进行搜索时,假设我们总是从编号最小的顶点出发,按编号递增的顺序 ...

  2. poj2065 SETI

    题目描述: 多组数据,每次给出一个模数$p$和一个表示答案的字符串, 相当于给出一个方程组:$$a_1*1^{1}+a_2*1^{2}+……+a_n*1^{n}=f_1$$ $$a_1*2^{1}+a ...

  3. 数组合并--php

    常用的合并数组方法有以下几种: 1  array_merge 2  '+' 3  array_merge_recursive 下面是一段对比的代码 $array1 = array(2,4," ...

  4. jdk环境变量配置至第一个简单程序运行成功

    桌面右键单击我的电脑,属性,高级,环境变量,然后再系统变量中配置(也可在用户变量中配置) 在配置环境变量时限查看是否已存在变量名称,有则添加路径,没有则创建再添加路径 /* JAVA_HOME% C: ...

  5. 标量子查询SQL改写

    一网友说下面sql跑的好慢,让我看看 sql代码: select er, cid, pid, tbl, zs, sy, (select count(sr.mobile_tele_no) from tb ...

  6. 项目中遇到的超卖问题及解决办法(使用go做测试工具)

    超卖问题:在一个很短的时间内,Mysql的数据状态在 取出,比较,提交,或修改中,另外一个进程访问数据导致的超卖问题. 案例: 1.前端没有做限制,如果用户连续点击签到,那么会有多条数据发送到后端,如 ...

  7. tornado框架基础02-输入和输出

    01 输出 write bytes类型 class IndexHandler(tornado.web.RequestHandler): def get(self): self.write(b'Torn ...

  8. 转载:CentOS7下部署Django项目详细操作步骤

    部署是基于:centos7+nginx+uwsgi+python3+django 之上做的 文章转自:Django中文网        https://www.django.cn/article/sh ...

  9. 【Codeforces 449A】Jzzhu and Chocolate

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 设最后行分成了x行,列分成了y列. 那么答案就是floor(n/x)floor(n/y) 然后x+y-2=k //即平均分配x行.y列 我们可 ...

  10. ZOJ - 3781 Paint the Grid Reloaded 题解

    题目大意: 给一个n*m的X O构成的格子,对一个点操作可以使与它相连通的所有一样颜色的格子翻转颜色(X—>O或O—>X),问给定的矩阵最少操作多少次可以全部变成一样的颜色. 思路: 1. ...