在.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. Visual Studio之Nuget(服务器)

    一.创建空Web项目 二.安装Nuget.Server 这个时候,安装完成后,发现项目文件里多了Default.aspx等文件,因为我选择的MVC,所以最好做下小小的调整 ——在Global.asax ...

  2. ASP.net MVC 无法初始化 ASP.NET Simple Membership 数据库

    1.错误信息 解决办法 1 更改Web.config的连接字符串 <connectionStrings> <add name="DefaultConnection" ...

  3. openwrt看IP流量

    可以利用iptable来实现的,看附件的脚本. 把他放到路由器里面,然后运行. #!/bin/sh echo "Collecting data..." echo "&qu ...

  4. Spring MVC返回的json如何去除根节点名称

    spring xml中配置视图如果是如下 <property name="defaultViews"> <list> <bean class=&quo ...

  5. JavaSE复习日记 : 抽象类

    /* * 抽象类 * 抽象: * 面向对象的三大核心思想; * 封装: 封装,ppp是封装的一部分 * 继承; * 多态: 父类的引用指向子类的对象 * 引用: 是指一个引用型变量 * 有哪些变量? ...

  6. istringstream和ostringstream的使用方法

    写程序用到istringstream和ostringstream,看了别人的博文,借鉴~~~~~~. iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆 ...

  7. 为什么IIS中找不到.net framework 4.5(转)

    .net 4.5是4.0的update,所以直接用4.0部署就可以了 .NET 4.5 is an in-place replacement for .NET 4.0, When .NET 4.5 i ...

  8. PHP判断是中文还是英文

    static function ischinese($s){ $allen = preg_match("/^[^/x80-/xff]+$/", $s); //判断是否是英文 $al ...

  9. Mysql innodb 后台的7大线程与3大内存

    A:一个master 线程(innodb 几乎在这个线程上实现有所有功能) B:一个lock 监控线程 C:一个错误监控线程 D:四个IO线程(insert buffer thread\log thr ...

  10. 深信服模式(先做减法,必须拜访客户三次、研究需求方向,把产品的问题控制住,快速反应,在未来十年,绝大部分业务都会搬到Internet上来,实现All on Internet)good

    深圳市盛凯信息科技有限公司与深信服合作多年,可以说是看着深信服“飞速”长大的.盛凯的总经理邓渊在采访中笑言:“他们(深信服)发展得太快,而我们发展得太慢.” 深信服的产品线已从最初只有VPN一条,到目 ...