使用@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.
- Import super classes
- Import parent classes (when you have children and parents)
- 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同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的细节问题的更多相关文章
- Python2.5-原理之模块
此部分来自于<Python学习手册>第五部分 一.模块(21章) 模块是最高级别的程序组织单元,它将程序代码和数据封装起来以便重用..模块往往对应于python程序文件.每个文件就是一个模 ...
- 扩展Python模块系列(二)----一个简单的例子
本节使用一个简单的例子引出Python C/C++ API的详细使用方法.针对的是CPython的解释器. 目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两 ...
- (三十三)Xcode项目的重要工程文件
1.Supporting files内有一个Xxx-Info.plist文件(旧版本Xcode的配置文件叫Info.plist).因此自定义的plist不要带Info关键词. 这个plist是系统的全 ...
- ES6模块import细节
写在前面,目前浏览器对ES6的import支持还不是很好,需要用bable转译. ES6引入外部模块分两种情况: 1.导入外部的变量或函数等: import {firstName, lastName, ...
- vue2.0实践的一些细节
最近用vue2.0做了个活动.做完了回头发现,好像并没有太多的技术难点,而自己好像又做了比较久...只能说效率有待提升啊...简单总结了一些比较细节的点. 1.对于一些已知肯定会有数据的模块,先用一个 ...
- Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)
Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚 使用场景: 1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动 ...
- java 性能优化:35 个小细节,让你提升 java 代码的运行效率
前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...
- package、import和import static
package 语句: 该语句必须作为源文件的第一条非注释性语句,一个源文件只能指定一个包,即只能包含一条package语句. import 和import static 关键字: 引入import关 ...
- Spring 学习笔记 4. 尚硅谷_佟刚_Spring_属性配置细节
1,字面值 •字面值:可用字符串表示的值,可以通过 <value> 元素标签或 value 属性进行注入. •基本数据类型及其封装类.String 等类型都可以采取字面值注入的方式 •若字 ...
随机推荐
- Visual Studio之Nuget(服务器)
一.创建空Web项目 二.安装Nuget.Server 这个时候,安装完成后,发现项目文件里多了Default.aspx等文件,因为我选择的MVC,所以最好做下小小的调整 ——在Global.asax ...
- ASP.net MVC 无法初始化 ASP.NET Simple Membership 数据库
1.错误信息 解决办法 1 更改Web.config的连接字符串 <connectionStrings> <add name="DefaultConnection" ...
- openwrt看IP流量
可以利用iptable来实现的,看附件的脚本. 把他放到路由器里面,然后运行. #!/bin/sh echo "Collecting data..." echo "&qu ...
- Spring MVC返回的json如何去除根节点名称
spring xml中配置视图如果是如下 <property name="defaultViews"> <list> <bean class=&quo ...
- JavaSE复习日记 : 抽象类
/* * 抽象类 * 抽象: * 面向对象的三大核心思想; * 封装: 封装,ppp是封装的一部分 * 继承; * 多态: 父类的引用指向子类的对象 * 引用: 是指一个引用型变量 * 有哪些变量? ...
- istringstream和ostringstream的使用方法
写程序用到istringstream和ostringstream,看了别人的博文,借鉴~~~~~~. iostream 标准库支持内存中的输入/输出,只要将流与存储在程序内存中的 string 对象捆 ...
- 为什么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 ...
- PHP判断是中文还是英文
static function ischinese($s){ $allen = preg_match("/^[^/x80-/xff]+$/", $s); //判断是否是英文 $al ...
- Mysql innodb 后台的7大线程与3大内存
A:一个master 线程(innodb 几乎在这个线程上实现有所有功能) B:一个lock 监控线程 C:一个错误监控线程 D:四个IO线程(insert buffer thread\log thr ...
- 深信服模式(先做减法,必须拜访客户三次、研究需求方向,把产品的问题控制住,快速反应,在未来十年,绝大部分业务都会搬到Internet上来,实现All on Internet)good
深圳市盛凯信息科技有限公司与深信服合作多年,可以说是看着深信服“飞速”长大的.盛凯的总经理邓渊在采访中笑言:“他们(深信服)发展得太快,而我们发展得太慢.” 深信服的产品线已从最初只有VPN一条,到目 ...