在開發(fā)中我們經常要定義常量,比如設定一個動畫執(zhí)行的時間,一般我們會這樣寫:
#define ANIMATION_DURATION 3.3
define 使用方便但是存在問題
宏作用在預編譯時期, 其真正的效果就是代碼替換, 而且是直接替換(內聯(lián)函數(shù)!!!).這條預處理命令會把源代碼中的ANIMATION_DURATION字符串替換為3.3.不過這樣做并不優(yōu)雅.原因是:
- 這樣定義出來的常量沒有類型信息,只是在預處理器里進行文本替換达吞,不做任何類型檢查
- 預處理命令會把所有的ANIMATION_DURATION全部替換成3.3(假設此命令聲明在頭文件里,那么所有引入這個頭文件的代碼,其ANIMATION_DURATION都會被替換)
更好的替代方案
有個辦法比用預處理命令來定義常量好,比如我們定義了一個類型為NSTimeInterval的常量:
static const NSTimeInterval kAnimationDuration = 3.3
- 注意常用的命名方法:
若常量局限于某"編譯單元"(實現(xiàn)文件.m)內,則在前面加字母k,若是常量在類之外可見,則通常以類名為前綴.
- 定義常量的位置(盡量放在.m文件中)
同時定義常量的位置也很重要,在頭文件里聲明預處理命令,常量名稱可能相互沖突時會產生問題.比如ANIMATION_DURATION這個常量名就不該用在頭文件中,因為所有引入此頭文件的其他文件中都會出現(xiàn)這個名字.static const定義的常量也不應該出現(xiàn)在頭文件里.因為OC中是沒有"名稱空間"這一概念的.所有這樣做等于聲明了一個kAnimationDuration全局變量.當然如果不打算公開某個常量.則應該將其定義到實現(xiàn)文件.m中.
static const 常量注意點
變量一定要同時使用static和const來聲明:
1.如果試圖修改由const修飾符聲明的變量,編譯器會報錯的,有時這正是我們想要的結果,我們期望的是一個定值不需要改變氓癌。
2.static修飾符意味著定義的變量僅在定義此變量的"編譯單元"可見.在OC中,"編譯單元"一詞通常指每個類的實現(xiàn)文件.m.因此在代碼中聲明一個kAnimationDuration變量,其作用域僅限于由CWGAnimatedView.m所生產的目標文件假如聲明時不帶static,那么編譯器就會創(chuàng)建一個外部符號,此時若另外一個編譯單元中也聲明了同名變量,那么編譯器就會拋出一條錯誤信息:
duplicate symbol _kAnimationDuration in:
CWGAnimatedView.o
CWGOtherView.o
3.有時候我們需要對外公開某個常量,使用extern
這個常量在頭文件中"聲明",且在實現(xiàn)文件中"定義".
// In the header file(.h)
extern NSString *const CWGViewStringConstant;
extern const NSTimeInterval CWGAnimationViewDuration;
// In the implementation file(.m)
NSString *const CWGViewStringConstant = @"VALUE";
const NSTimeInterval CWGAnimationViewDuration = 3.3;
使用上述定義常量的方法,編譯器會確保常量值不變,而采用#define預處理指令定義的常量可能會無意中被他人修改.從而導致各個部分引用的值不同,引起難以排查的bug.
總結:
- 不要使用#define預處理指令來定義常量,因為這樣的常量不包括類型信息,編譯器只會在編譯前執(zhí)行查找和替換操作,即使有人重新定義了常量值,編譯器也不會發(fā)出警告,這樣會導致應用程序中的常量值不一致.
- 在實現(xiàn)文件.m中使用static const來定義"只在編譯單元張可見的常量",由于此類常量不在全局符號表中,所以無須為其名稱加前綴.
- 在頭文件中使用extern來聲明全局常量,并在相關文件中定義其值,這種常量要出現(xiàn)在全局符號表中,所以其名稱應該加以區(qū)分,通常用與之相關的類名做前綴.