与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. Mac下复制粘贴的快捷键是什么?随记

    刚从window换成Mac OS系统的用户对于一些常用的快捷键一定非常的不习惯,“mac复制粘贴快捷键是什么?”这一简单的问题相信很多刚刚从Windows平台转到Mac平台的用户会问到的问题,因为Ma ...

  2. requests 模块笔记

    import requests 请求方式: requests.get("https://www.baidu.com") requests.post("http://htt ...

  3. 剑指Offer(书):二维数组中的查找

    题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  4. 快速简单高效的搭建 SolrCloud 集群

    转https://segmentfault.com/a/1190000008634902 集群配置 集群中的每台机器都要按照以下说明进行配置启动 首先到 solr 安装目录的 bin 下,编辑 sol ...

  5. HDU 1242 Rescue 营救天使

    Description Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The prison is describe ...

  6. Device eth0 does not seem to be present,delaying initialization问题

    1.打开/etc/udev/rules.d/70-persistent-net.rules: cat /etc/udev/rules.d/70-persistent-net.rules 文件内容如图: ...

  7. 关于构造函数什么值传递给他的实例,只有this和prototype

    var a= function (){var bb = 12; this.aa ="xxx"}; a.aa="www"; a.prototype.cc=&quo ...

  8. 『NYIST』第九届河南省ACM竞赛队伍选拔赛[正式赛二]- Nearly Lucky Number(Codeforces Beta Round #84 (Div. 2 Only)A. Nearly)

    A. Nearly Lucky Number time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  9. Web性能压力测试工具——Siege

    最近需要对Istio里的bookinfo进行分析,老是手去刷新太慢了,就找了个自动点的工具——Siege Siege是一款开源的压力测试工具,设计用于评估WEB应用在压力下的承受能力.可以根据配置对一 ...

  10. 机器学习基础-Logistic回归1

    利用Logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类. 训练分类器时的做法就是寻找最佳拟合参数,使用的时最优化算法. 优点:计算代价不高,利于理解和实现. ...