我們在ios開發中都會經常定義常量,比如說,在一個頁面上播放一個動畫,在很多開發者來說都會把八方動畫的時間提取為一個常量,我想想很多人都會這樣做,包括我自己。
define ANIMATION_DURATTON 1.5
這段預處理指令會吧代碼中的ANIMATION_DURATTON字符串替換成1.5,不過這樣定義出來的常量沒有類型信息,預處理過程中會把碰到的所有的ANIMATION_DURATTON替換成為1.5,這樣的話,假設此指令生命在某個頭文件中,那么所有引入這個頭文件的代碼,其ANIMATION_DURATTON都會被替換。
想要解決這個問題最好的辦法就是設法利用編譯器的某些特性,有個辦法比用預處理指令來定義常量更好,如例:
static const NSSTimeInterval kAnimationDuration = 1.5;
此方式定義的常量包含類型信息,其好處是清楚的描述了常量的含義,由此可以知道該常量的類型為NSSTimeInterval。
定義常量的位置很重要,我們總喜歡在頭文件里聲明預處理指令,這樣做真的很糟糕,當常量名稱有可能互相沖突時更是如此。例如,ANIMATION_DURATTON這個常量名就不該定義在頭文件,應為所喲引入這份頭文件的其他文件都會出現這個名字。其實就連用static const定義的那個常量也不應該出現在頭文件里。因為Objective-C沒有“名稱空間”這一概念,所以那樣做就等于聲明了一個名叫kAnimationDuration全局變量,因此名稱必須加上前綴,以表明所屬的類。
如果不打算公開某個常量,就應該在使用該常量的實現文件里定義,例如:
//EOCAnimatedView.h
import <UIKit/UIKit.h>
@interface EOCAnimatedView : UIView
-(void)animate;
@end
//EOCAnimatedView.m
import "EOCAnimatedView.h"
static const NSSTimeInterval kAnimationDuration = 1.5;
@implementation EOCAnimatedView
-(void)animate{
}
@end
變量一定要同時用static和const來聲明,事實上,一個變量既聲明為static,又聲明為const,那么編譯器根本不會創建符號,而會像#define預處理指令一樣把所有遇到的變量都替換成常值,不過要記住,用這種方式定義的常量帶有類型信息。
有時候需要對外公開一個常量,此類常量需要放在“全局符號表”中,以便可以在定義該常量的編譯單元之外使用,因此其定義方式與上例演示的static const有所不一樣:
//In the header file
extern NSString *const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant=@"VALUE";
這個常量在頭文件中“聲明”,且在實現文件中“定義”,const修飾符在常量類型中的位置,常量從右至左解讀。編譯器看到頭文件中的extern關鍵字,就能明白如何明白在引入此文件的代碼中處理該常量了。這個關鍵字就會告訴編譯器,在全局符號表中將會有一個名叫EOCStringConstant的符號,編譯器無需查看其定義,既允許此代碼使用此常量,因為它知道當鏈接成二進制文件之后,肯定能找到這個常量。
此類常量必須要定義,且只能定義一次,通常將其定義在與聲明該常量的頭文件相關的實現文件里,由實現文件生成目標文件時,編譯器會在“數據段”為字符串分配存儲空間。連接器會把此目標文件和其他的目標文件相鏈接,以生成最終的二進制文件,凡使用到EOCStringConstant這個全局符號的地方,鏈接器都能將其解析。
注意常量的名字,為避免名稱沖突,最好使用與之相關的類名作為前綴。
這樣定義的常量優于使用#define預處理命令,因為編譯器確保常量的值不會變,總之勿使用預處理指令定義常量,而應該用戒指編譯器的特性來確保常量的正確。