zoukankan      html  css  js  c++  java
  • 使用@class和#import的细节问题

    在.h头文件中导入其它头文件可以使用#import语句,从而在该头文件下使用另一个文件中的类和成员,但是我在使用#import语句时却遇到了以下问题:

    首先写一个ViewController类:

    #import <UIKit/UIKit.h>
    #import "NewViewController.h"
    
    @interface ViewController : UIViewController
    @property (strong, nonatomic) NewViewController *nvc;
    @end


    该类包含了属性变量nvc,其类型为NewViewController类。注意上面导入了NewViewController.h文件。

    另外还有一个NewViewController类:

    #import <UIKit/UIKit.h>
    #import "ViewController.h"
    
    @interface NewViewController : UIViewController
    @property (strong, nonatomic) ViewController *vc;
    @end


    该类包含了属性变量vc,其类型为ViewController类。注意该类的头文件也导入了ViewController.h文件。

    问题出现了:



    在编译时编译器发出无法识别ViewController类和NewViewController类的报错。

    个人看来:原因是ViewController类在声明时定义了一个NewViewController类成员,而该类在声明时又定义了一个ViewController类成员,从而导致了循环定义。ViewController类需要NewViewController类完成定义才能正确实现定义,而NewViewController类又需要ViewController类完成定义才能正确实现定义,最终两者均无法完成接口的声明,编译器发出无法识别两个类的报错。


    解决方法:

    #import <UIKit/UIKit.h>
    
    @class NewViewController;
    
    @interface ViewController : UIViewController
    @property (strong, nonatomic) NewViewController *nvc;
    @end
    #import <UIKit/UIKit.h>
    
    @class ViewController;
    
    @interface NewViewController : UIViewController
    @property (strong, nonatomic) ViewController *vc;
    @end


    将以上两个类的头文件的导入语句改成@class声明就可以了。

    原因是@class只是声明了一个类,关于该类的实现等任何细节编译器暂时不需要理会,从而编译器不会报错或者警告。

    另外要使用这两个类时,必须在.m文件中导入对应的.h头文件。



    在解决问题的过程中在网上查了一些资料,看到了关于@class和#import语句使用的一些细节,发现自己写程序的过程中还是有很多细节是需要注意的。

    下面的内容参考了这个链接:http://blog.sina.com.cn/s/blog_a843a8850101b6a7.html

    很多刚开始学习iOS开发的同学可能在看别人的代码的时候会发现有部分#import操作写在m文件中,而h文件仅仅使用@class进行声明,不禁纳闷起来,为什么不直接把#import放到h文件中呢?

    这是因为h文件在修改后,所有import该h文件的所有文件必须重 新build,因此,如果把#import写在h文件中,import该h文件的文件也就会产生不必要的编译,增加编译时间,特别是在项目文件多的情况 下。想象一下,如果只是修改一个h文件而导致上百个文件不必要的编译,那是一件多么让人纠结的事情。。。

    对于@class只是告诉编译器有这个class,请不要报错或警告,因此不会给编译造成影响。

    什么时候用@class这种方式声明比#import好呢?

    stackoverflow上的高手们给了不少建议:

    Randy Marsh:

    When I develop, I have only three things in mind that never cause me any problems.

    1. Import super classes
    2. Import parent classes (when you have children and parents)
    3. Import classes outside your project (like in frameworks and libraries)

    For all other classes (subclasses and child classes in my project self), I declare them via forward-class.

     

    Justin:

    Simple answer: You #import or #include when there is a physical dependency. Otherwise, you use forward declarations (@class MONClass ,struct MONStruct , @protocol MONProtocol ).

    Here are some common examples of physical dependence:

    • Any C or C++ value (a pointer or reference is not a physical dependency). If you have aCGPoint as an ivar or property, the compiler will need to see the declaration ofCGPoint .
    • Your superclass.
    • A method you use.

    最后,我建议还是养成良好的import习惯,不要偷懒都把import放在h文件中,无论参与的项目大小,养成良好的编程习惯非常重要。

    下面来说一下#import同class之间的区别

         在ios中我们经常会在.h和.m中引入一些类啊等等一般用的是#import来进行声明,你们可能也见到在.h文件进用@class来声明的,那么#import和@class进行声明 到底有什么的区别呢?下面我来说说

      1.import会包含这个类的所有信息,包括实体变量和方法,而@class只告诉编 译器,声明的类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你,所以在头文件中如果用@class声明某个类后,在.m的实现中如果用到声明类的具体方法或变量时还得再#import类  

    2.在.h头文件中进行声明时用#import的话,如果100个头文件都#import同一个头件,或者这些文件是依次引用的,如A->B,B->C,C->D,当最开始的那个头文件有变化后进行编译时,后面所有引用它的类都需要重新编译,如果引用最开始的头文件的类很多的话,那么这将耗费大量的时间,而用@class则不会,可能有人会想即然.h只是用@class只是简单的一个声明告编译器有这个类不让其报错,那么.m中要用到引入的类的方法和属性时,不还是要#import头文件一次,是的这个是对的,但编译器编译的时候只编译头文件的,所以你的.m中用#import与编译时间没太大关系

    3.接下来说说什么时候该用@class,什么时候该用#import进行声明,

      (1)一般如果有继承关系的用#import,如B是A的子类那么在B中声明A时用#import

      (2) 另外就是如果有循环依赖关系,如:A->B,B->A这样相互依赖时,如果在两个文件的头文件中用#import分别声明对方,那么就会出现头文件循环利用的错误,这时在头文件中用@class声明就不会出错

     (3)还有就是自定义代理的时候,如果在头文件中想声明代理的话如@interface SecondViewController:UIViewController时应用#import不然的话会出错误,注意XXXXDelegate是自定义的

    其中我比较赞同Randy Marsh的观点,只在以下情况下在.h文件中导入其它类的头文件:

    1.需要使用直接父类super classes

    2.需要在子类中使用父类parent classes(在这里super classes和parent classes的分别应该是在于super classes指的是直接父类,parent classes指的是所有父类,即直接父类,父类的父类等等)

    3.需要使用框架或类库frameworks and libraries中的类,如#import <UIKit/UIKit.h>

    需要补充的是(上文也提到了):如果在ViewController中自定义了委托:

    #import <UIKit/UIKit.h>
    
    @protocol ViewControllerDelegate <NSObject>
    
    @end
    
    @class NewViewController;
    
    @interface ViewController : UIViewController
    @property (strong, nonatomic) NewViewController *nvc;
    @property (weak, nonatomic) id <ViewControllerDelegate> vcDelegate;
    @end


    并且NewViewController要实现该委托:

    #import <UIKit/UIKit.h>
    
    @class ViewController;
    
    @interface NewViewController : UIViewController <ViewControllerDelegate>
    @property (strong, nonatomic) ViewController *vc;
    @end


    那么编译器也会报错:


    单纯的@class无法识别该类中定义的委托,解决办法:

    #import <UIKit/UIKit.h>
    #import "ViewController.h"
    
    // @class ViewController;
    
    @interface NewViewController : UIViewController <ViewControllerDelegate>
    @property (strong, nonatomic) ViewController *vc;
    @end


    导入ViewController.h文件。

    也就是要如果NewViewController要实现ViewController中自定义的委托,那么必须使用#import语句导入ViewController.h文件。


    另外上文也非常强调重复导入头文件带来的效率问题:如果在许多不同的.h文件中大量重复导入同一个.h文件,那么该.h文件的修改将导致引用该类的所有类的头文件都要重新进行编译,这样将大幅影响编译效率。

    因此以后编程的时候必须慎用@class和#import,不要偷懒将所有的.h文件都使用#import语句塞入同一个.h文件中。好的编程习惯将有利于提高整个程序的效率。


  • 相关阅读:
    Android 传感器应用
    WebStrom9 体验nodejs
    Web前端框架 小记
    接入淘宝API(PHP版本)
    Android SDK 国内镜像
    Ubuntu14.04 搭建 node.js 环境(Binaries方式)
    C# 异常类型及对应异常类
    .net中序列化读写xml方法的总结
    ASp.NET Core Centos7运行环境搭建
    Linux Centos 常用命令
  • 原文地址:https://www.cnblogs.com/riskyer/p/3327538.html
Copyright © 2011-2022 走看看