C#寫多了都忘記有前置聲明(Forward Declaration)這麼回事了,看到@class的時候楞了半天。今天就寫這個主題吧。
1. 為什麼需要前置聲明
前置聲明有助於避免循環相依性。像:
interface A:NSObject- (B*)calculateMyBNess;@end@interface B:NSObject- (A*)calculateMyANess; @end
這樣聲明無法編譯,因為會遇到先有雞還是先有蛋的問題。
這時候就需要加一個前置聲明:
@class B;@interface A:NSObject- (B*)calculateMyBNess;@end@interface B:NSObject- (A*)calculateMyANess; @end
@class告知編譯器,在某個地方有叫這樣名字的一個類存在。
2. @class vs. #import
從文法上,使用前置聲明和使用#import都能編譯通過與運行成功。
那麼,這兩者分別適用什麼場合?
根據http://stackoverflow.com/questions/322597/class-vs-import,
如果你看到警告:
warning: receiver 'myCoolClass' is a forward class and corresponding @interface may not exist
就需要import這個檔案了。不過可以不是直接在.h(標頭檔)裡import,而是在.m(implementation檔案)裡import,在標頭檔裡使用@class聲明。
@class通常可以使你不用過早地import。如果編譯器看到了一行文法:
@class myCoolClass,它就知道了自己可能馬上會看到類似如下的代碼:
myCoolClass *myObject;
於是它會為這個類保留一個指標的空間,然後忙其他的去了。
不過如果你需要建立或訪問myObject的成員,那麼僅僅一個類指標就不夠了。你需要讓編譯器知道這些成員到底是什麼。這時候就需要#import "myCoolClass.h"了。
有人簡單列了三條規則。由於水平不夠,翻譯的話可能會導致歧義,我直接放原文:
- Only #import the super class, and adopted protocols, in header files.
- #import all classes, and protocols, you send messages to in implementation.
- Forward declarations for everything else.
在http://stackoverflow.com/questions/6076207/objective-c-forward-declarations-vs-imports
有人提到過,根據他的經驗,用#import不慎,有可能會讓編譯器多編譯N多代碼(他用了million這個數量級)。只要有一個標頭檔被稍微修改,所有import的類都需要重新編譯,於是拖長了編譯時間。
3. 為什麼C#不需要前置聲明?
我個人Google了一下,沒有找到相關的解釋。看起來幾乎沒人對C#為什麼沒有前置聲明感興趣。。。
關於這個問題我和朋友Zero討論了一下,他的意見如下:
“C# compiler能夠多遍掃描原始碼所以不需要任何前置聲明。理論上C++ compiler也可以,不過事實上C++ compiler沒有這麼做而已。”
當然C#也會有循環相依性。這種時候可以用依賴注入(控制反轉)來消除,具體可以參見如下:
http://stackoverflow.com/questions/3955465/circular-class-reference-problem