• 在类的头文件里尽量少引入其它头文件 <<Effective Objective-C>>


    与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 分类”中。

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

  • 相关阅读:
    Java内存回收
    Android四大基本组件介绍与生命周期
    JAVA中获取当前系统时间
    jquery如何判断元素是否被点击_百度知道
    css控制div显示/隐藏方法及2种方法比较原码
    点击图标不断震动效果
    jquery 如何动态添加、删除class样式方法介绍_jquery_脚本之家
    HTML5绘制矩形和圆形并且还有获取在这个图层内的坐标的思路和代码
    Attribute name invalid for tag form according to TLD异常解决办法_gaigai_百度空间
    html5绘图
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7238012.html
Copyright © 2020-2023  润新知