在.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文件中。好的编程习惯将有利于提高整个程序的效率。

使用@class和#import的细节问题的更多相关文章

  1. Python2.5-原理之模块

    此部分来自于<Python学习手册>第五部分 一.模块(21章) 模块是最高级别的程序组织单元,它将程序代码和数据封装起来以便重用..模块往往对应于python程序文件.每个文件就是一个模 ...

  2. 扩展Python模块系列(二)----一个简单的例子

    本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...

  3. (三十三)Xcode项目的重要工程文件

    1.Supporting files内有一个Xxx-Info.plist文件(旧版本Xcode的配置文件叫Info.plist).因此自定义的plist不要带Info关键词. 这个plist是系统的全 ...

  4. ES6模块import细节

    写在前面,目前浏览器对ES6的import支持还不是很好,需要用bable转译. ES6引入外部模块分两种情况: 1.导入外部的变量或函数等: import {firstName, lastName, ...

  5. vue2.0实践的一些细节

    最近用vue2.0做了个活动.做完了回头发现,好像并没有太多的技术难点,而自己好像又做了比较久...只能说效率有待提升啊...简单总结了一些比较细节的点. 1.对于一些已知肯定会有数据的模块,先用一个 ...

  6. Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)

    Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚 使用场景: 1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动 ...

  7. java 性能优化:35 个小细节,让你提升 java 代码的运行效率

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  8. package、import和import static

    package 语句: 该语句必须作为源文件的第一条非注释性语句,一个源文件只能指定一个包,即只能包含一条package语句. import 和import static 关键字: 引入import关 ...

  9. Spring 学习笔记 4. 尚硅谷_佟刚_Spring_属性配置细节

    1,字面值 •字面值:可用字符串表示的值,可以通过 <value> 元素标签或 value 属性进行注入. •基本数据类型及其封装类.String 等类型都可以采取字面值注入的方式 •若字 ...

随机推荐

  1. wcf xml消息客户端cookie

    public override bool Login(string userName, string password) { using (HttpClient client = new HttpCl ...

  2. A - FatMouse' Trade

    Description FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the wareho ...

  3. Linux流量监控工具使用总结 - iftop

    在类Unix系统中可以使用top查看系统资源.进程.内存占用等信息.查看网络状态可以使用netstat.nmap等工具.若要查看实时的网络流量,监控TCP/IP连接等,则可以使用iftop. 一.if ...

  4. 关于Scrapy框架的基本概念

    Scrapy爬取网页基本概念 Scrapy爬取网页基本概念 怎么样用Scrapy生成project? scrapy startproject xxx 如何用Scrapy爬取网页? import scr ...

  5. Maven手动创建多模块项目

    Maven手动创建多模块项目 我要创建的项目名称是:unicorn,项目包含两个模块,分别是unicorn-core和unicorn-web.包的路径是com.goldpalm.tour. 项目创建流 ...

  6. Apache服务无法启动的解决方法

    apache服务无法启动的解决方法 在配置apache的时候,把apache安装为服务myweb,用apacheMonitor启动myweb发现无法启动,提示:the requested operat ...

  7. rdo(remote data objects) repo openstack icehouse

    problem making ssl connection Error: Cannot retrieve repository metadata (repomd.xml) for repository ...

  8. 飘逸的python - 用urlparse从url中抽离出想要的信息

    最近有个需求,要检测配置中的那些url的域名是否都正常,即是否都能ping通. 不过配置中url格式是这样的 http://www.something.com:1234/ . 要ping的是www.s ...

  9. Java Native Interface Specification(JNI)

    Java Native Interface Specification(JNI) 使用场景: 需要的功能,标准的java不能提供 有了一个用其他的语言写好的工具包,希望用java去访问它 当需要高性能 ...

  10. 发送请求工具—Advanced REST Client

    Advanced REST Client是Chrome浏览器下的一个插件,通过它能够发送http.https.WebSocket请求.在Chrome商店下搜索Advanced REST Client, ...