第二条 :
一: 利用@class在类的头文件中可以减少编译时间
- 当我们用Xcode创建类时, 其代码看上去如下
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
// Person.m
#import "Person.h"
@implementation Person
@end
用OC编写类几乎都要引入Foundation.h. 如果不在该类中引入这个文件, 那么就要引入与其父类所属框架相对应的"基本头文件", 例如: 在创建IOS应用程序时, 通常会集成UIViewController类, 而这些子类的头文件需要引入UIKit.h, 集成UIViewController哪些类, 可能会用到UIKit中的许多内容
- 这段时间如果创建了一个名为Dog的新类, 让每个Person实例都拥有一个Dog. 于是代码如下:
// Person.h
import <Foundation/Foundation.h>
#import "Dog.h"
@interface Person : NSObject
/** Dog */
@property (nonatomic,strong) Dog *dog;
/** name */
@property (nonatomic,strong) NSString *name;
@end
- 注意: 这么做不够优雅, 在编译使用了一个Person类的文件时, 不需要知道Dog类的全部细节, 只需要知道有一个类名叫Dog就好, 这时, 我们就可以把这种情况告诉编译器, 用@class,代码如下:
// Person.h
#import <Foundation/Foundation.h>
@class Dog;
@interface Person : NSObject
/** Dog */
@property (nonatomic,strong) Dog *dog;
/** name */
@property (nonatomic,strong) NSString *name;
@end
- 这叫做"向前声明"(forward declaring)该类
如果Person的实现文件要使用Dog类, 则必须知道所有接口, 这时实现文件代码就如下:
// Person.m
#import "Person.h"
#import "Dog.h"
@implementation Person
@end
- 只有在真的需要时才引入, 这样就可以减少类的使用者所需引入的头文件的数量, 上例中, 把Dog.h引入到Person.h,那么只要引入Dog.h, 就会一并引入Dog.h的所有内容, 这会增加编译时间, 如果使用@class, 则不需要引入很多根本用不到的内容, 这会大大减少编译时间
二: 利用@class可以避免循环引用
1.假设要为Dog类加入新增及删除Master的方法, 那么其头文件中会加入下述方法:
- (void)addMaster:(Person *)person;
- (void)removeMaster:(Person *)person;
这时, 编译器若要编译Dog.h, 那么编译器必须知道Person, 这个类, 而要编译Person.h, 则必须又要知道Dog这个类, 俩个类的声明文件如下(以下情况没有用@class):
// Person.h
import <Foundation/Foundation.h>
#import "Dog.h"
@interface Person : NSObject
/** dog */
@property (nonatomic,strong) Dog *dog;
@end
// Dog.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interface Dog : NSObject
- (void)addMaster:(Person *)person;
- (void)removeMaster:(Person *)person;
@end
- 上面代码会造成, "循环引用"(chicken-and-egg situation), 当解析其中一个头文件时, 编译器会发现它引入了另一个头文件, 而那个头文件又回过头来引用第一个头文件,使用#import而非#include指令虽然不会导致死循环, 这意味着俩个类有一个无法被正确编译, 这时候就需要用到@class, 将其中一个改为使用@class 例如:
// Dog.h
#import <Foundation/Foundation.h>
@class Person;
@interface Dog : NSObject
- (void)addMaster:(Person *)person;
- (void)removeMaster:(Person *)person;
@end
三: 有时候必须要在头文件中, 引入其他头文件
- 如果你写的类, 集成某个类, 则必须引入定义那个父类的头文件
- 或者是你声明的类遵从某个协议(protocol), 那么该协议必须有完整定义, 而且不能用向前声明, 向前声明只能告诉编译器有某个协议, 而此时编译器却需要知道该协议中定义的方法, 例如:
一个Student继承自Person, 并且遵守<SchoolDelegate>协议
#import <Foundation/Foundation.h>
#import "Person.h"
#import "SchoolDelegate.h"
@interface Student : Person <SchoolDelegate>
@end
* 仔细看上述代码就会发现, student类引入了俩个头文件, 其中一个是 SchoolDelegate.h文件, 这个文件里面放存放了协议, 代码如下:
```
#import <Foundation/Foundation.h>
@protocol SchoolDelegate <NSObject>
- (void)buyTicket: (id *)stu;
@end
```
* 这样将协议单独放到一个文件中虽然也是一种方法, 但是会增加编译时间
* (delegate protocol)这时候可以将协议与接收协议的类放在一起定义, 最好能在实现文件中声明此类实现了该代理方法, 并把实现代码放在"分类", 这样的话, 只要在实现文件中引入包含协议的头文件即可, 不需要将这个文件放在公共头文件里, 代码如下:
//Student.m 文件
#import "Student.h"
@protocol SchoolDelegate <NSObject>
- (void)buyTicket:(id)stu;
@end
@implementation Student
@end
重点:
- 每次在头文件中引入其他头文件之前, 都要先问问自己这样做是否有必要, 如果可以用向前声明取代引入, 那么最好用向前声明, 若因为实现属性, 实例变量, 或者要遵守协议而必须引入头文件, 则尽量将其移到"class-continuation分类"中
- 这样做的好处, 不近可以缩减编译时间, 而且还能降低彼此依赖程序,
- 除非有必要, 否则不要引入头文件, 一般再来, 应在某个类的头文件中使用向前声明来提及别的类, 并在实现文件中引入那些类的头文件, 这样做可以降低耦合
- 有时无法使用向前声明, 比如遵守协议, 这种情况下, 尽量把"该类遵循某协议"的这条声明移到"class-continuation分类"中, 如果不行的话, 就把协议单独放在一个头文件
声明: 以上大部分内容均来自 Effective Objective-C