第二條 :
一: 利用@class在類的頭文件中可以減少編譯時間
- 當我們用Xcode創(chuàng)建類時, 其代碼看上去如下
// Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@end
// Person.m
#import "Person.h"
@implementation Person
@end
用OC編寫類幾乎都要引入Foundation.h. 如果不在該類中引入這個文件, 那么就要引入與其父類所屬框架相對應的"基本頭文件", 例如: 在創(chuàng)建IOS應用程序時, 通常會集成UIViewController類, 而這些子類的頭文件需要引入UIKit.h, 集成UIViewController哪些類, 可能會用到UIKit中的許多內容
- 這段時間如果創(chuàng)建了一個名為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
- 注意: 這么做不夠優(yōu)雅, 在編譯使用了一個Person類的文件時, 不需要知道Dog類的全部細節(jié), 只需要知道有一個類名叫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的實現(xiàn)文件要使用Dog類, 則必須知道所有接口, 這時實現(xiàn)文件代碼就如下:
// Person.m
#import "Person.h"
#import "Dog.h"
@implementation Person
@end
- 只有在真的需要時才引入, 這樣就可以減少類的使用者所需引入的頭文件的數(shù)量, 上例中, 把Dog.h引入到Person.h,那么只要引入Dog.h, 就會一并引入Dog.h的所有內容, 這會增加編譯時間, 如果使用@class, 則不需要引入很多根本用不到的內容, 這會大大減少編譯時間
二: 利用@class可以避免循環(huán)引用
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
- 上面代碼會造成, "循環(huán)引用"(chicken-and-egg situation), 當解析其中一個頭文件時, 編譯器會發(fā)現(xiàn)它引入了另一個頭文件, 而那個頭文件又回過頭來引用第一個頭文件,使用#import而非#include指令雖然不會導致死循環(huán), 這意味著倆個類有一個無法被正確編譯, 這時候就需要用到@class, 將其中一個改為使用@class 例如:
// Dog.h
#import <Foundation/Foundation.h>
@class Person;
@interface Dog : NSObject
- (void)addMaster:(Person *)person;
- (void)removeMaster:(Person *)person;
@end
三: 有時候必須要在頭文件中, 引入其他頭文件
- 如果你寫的類, 集成某個類, 則必須引入定義那個父類的頭文件
- 或者是你聲明的類遵從某個協(xié)議(protocol), 那么該協(xié)議必須有完整定義, 而且不能用向前聲明, 向前聲明只能告訴編譯器有某個協(xié)議, 而此時編譯器卻需要知道該協(xié)議中定義的方法, 例如:
一個Student繼承自Person, 并且遵守<SchoolDelegate>協(xié)議
#import <Foundation/Foundation.h>
#import "Person.h"
#import "SchoolDelegate.h"
@interface Student : Person <SchoolDelegate>
@end
* 仔細看上述代碼就會發(fā)現(xiàn), student類引入了倆個頭文件, 其中一個是 SchoolDelegate.h文件, 這個文件里面放存放了協(xié)議, 代碼如下:
```
#import <Foundation/Foundation.h>
@protocol SchoolDelegate <NSObject>
- (void)buyTicket: (id *)stu;
@end
```
* 這樣將協(xié)議單獨放到一個文件中雖然也是一種方法, 但是會增加編譯時間
* (delegate protocol)這時候可以將協(xié)議與接收協(xié)議的類放在一起定義, 最好能在實現(xiàn)文件中聲明此類實現(xiàn)了該代理方法, 并把實現(xiàn)代碼放在"分類", 這樣的話, 只要在實現(xiàn)文件中引入包含協(xié)議的頭文件即可, 不需要將這個文件放在公共頭文件里, 代碼如下:
//Student.m 文件
#import "Student.h"
@protocol SchoolDelegate <NSObject>
- (void)buyTicket:(id)stu;
@end
@implementation Student
@end
重點:
- 每次在頭文件中引入其他頭文件之前, 都要先問問自己這樣做是否有必要, 如果可以用向前聲明取代引入, 那么最好用向前聲明, 若因為實現(xiàn)屬性, 實例變量, 或者要遵守協(xié)議而必須引入頭文件, 則盡量將其移到"class-continuation分類"中
- 這樣做的好處, 不近可以縮減編譯時間, 而且還能降低彼此依賴程序,
- 除非有必要, 否則不要引入頭文件, 一般再來, 應在某個類的頭文件中使用向前聲明來提及別的類, 并在實現(xiàn)文件中引入那些類的頭文件, 這樣做可以降低耦合
- 有時無法使用向前聲明, 比如遵守協(xié)議, 這種情況下, 盡量把"該類遵循某協(xié)議"的這條聲明移到"class-continuation分類"中, 如果不行的話, 就把協(xié)議單獨放在一個頭文件
聲明: 以上大部分內容均來自 Effective Objective-C