OC基本功(一)使用@class和#import的细节问题
在.h头文件中导入其它头文件可以使用#import语句,从而在该头文件下使用另一个文件中的类和成员,但是我在使用#import语句时却遇到了以下问题:
首先写一个ViewController类:
- #import <UIKit/UIKit.h>
- #import "NewViewController.h"
- @interface ViewController : UIViewController
- @property (strong, nonatomic) NewViewController *nvc;
- @end
#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
#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 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
#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
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之间的区别
2.在.h头文件中进行声明时用#import的话,如果100个头文件都#import同一个头件,或者这些文件是依次引用的,如A->B,B->C,C->D,当最开始的那个头文件有变化后进行编译时,后面所有引用它的类都需要重新编译,如果引用最开始的头文件的类很多的话,那么这将耗费大量的时间,而用@class则不会,可能有人会想即然.h只是用@class只是简单的一个声明告编译器有这个类不让其报错,那么.m中要用到引入的类的方法和属性时,不还是要#import头文件一次,是的这个是对的,但编译器编译的时候只编译头文件的,所以你的.m中用#import与编译时间没太大关系
3.接下来说说什么时候该用@class,什么时候该用#import进行声明,
其中我比较赞同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
#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
#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
#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文件中。好的编程习惯将有利于提高整个程序的效率。
版权声明:本文为博主原创文章,未经博主允许不得转载。