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