第一章 熟悉Objective-C—第4條:多用類型常量谢揪,少用#define預處理指令

編寫代碼時經常要定義變量。例如:

#define ANIMATION_DURATION 0.3

上述預處理指令會把源代碼中的ANIMATION_DURATION字符串替換為0.3竖螃。這可能就是你想要的效果,不過這樣定義出來的常量沒有類型信息逗余。"持續(xù)"(duration)這個詞看上去應該與時間有關特咆,但是代碼中又未明確指出。此外录粱,預處理過程會把碰到的所有ANIMATION_DURATION一律替換成0.3腻格,這樣的話,假設此指令聲明在某個頭文件中啥繁,那么所有引入了這個頭文件的代碼菜职,其ANIMATION_DRRATION都會被替換。
要想解決此問題旗闽,應該設法利用編譯器的某些特性才對酬核。有個辦法比用預處理指令來定義常量更好。比方說适室,下面這行代碼就定義了一個類型為NSTimeInterval的常量:

static const NSTimeInterval kAnimationDuration = 0.3;

請注意嫡意,用此方式定義的常量包含類型信息,其好處是清楚地描述了常量的含義捣辆。由此可知該常量類型為NSTimeInterval蔬螟,這有助于為其編寫開發(fā)文檔。如果要定義許多常量罪帖,那么這種方式能令稍后閱讀代碼的人更易理解其意圖促煮。
還要注意常量名稱。常用的命名法是:若常量局限于某"編譯單元"(translation unit整袁,也就是"實現文件"菠齿,implementation file)之內,則在前面加字母k坐昙;若常量在類之外可見绳匀,則通常以類名為前綴。
定義常量的位置很重要炸客。我們總喜歡在頭文件里聲明預處理命令疾棵,這樣做真的很糟糕,當常量名稱有可能互相沖突時更是如此痹仙。例如是尔,ANIMATION_DURATION這個常量名就不該用在頭文件中,因為所有引入了這份頭文件的其他文件中都會出現這個名字开仰。其實就連用static const定義的那個常量也不該出現在頭文件里拟枚。因為Objective-C沒有"命名空間"(namespace)這一概念薪铜,所以那樣做等于聲明了一個名叫kAnimationDuration的全局變量。此名稱應該加上前綴恩溅,以表明其所屬的類隔箍,例如可改為EOCViewClassAnimationDuration。
若不打算公開某個常量脚乡,則應將其定義在使用該常量的實現文件里蜒滩。比方說,要開發(fā)一個使用UIKit框架的iOS應用程序奶稠,其UIView子類中含有表示動畫播放時間的常量俯艰,那么可以這樣寫:

//EOCAnimatedView.h
#import <UIKit/UIKit.h>

@interface EOCAnimatedView : UIView
- (void)animate;
@end

//EOCAnimatedView.m
#import "EOCAnimatedView.h"

static const NSTimeInterval kAnimationDuration = 0.3;

@implementation EOCAnimatedView
- (void)animate {
  [UIView animateWithDuration:kAnimationDuration
                        animations:^(){
                          //Perform animations
      }];
}
@end

變量一定要同時用static與const來聲明。如果試圖修改由const修飾符所聲明的變量锌订,那么編譯器就會報錯蟆炊。在本例中,我們正是希望這樣:因為動畫播放時長為定值瀑志,所以不應修改。而static修飾符則意味著該變量僅在定義此變量的編譯單元中可見污秆。編譯器每收到一個編譯單元劈猪,就會輸出一份"目標文件"(object file)。在Objective-C的語境下良拼,"編譯單元"一詞通常指每個類的實現文件(以.m為后綴名)战得。因此,在上述范例代碼中聲明的kAnimationDuration變量庸推,其作用域僅限于由EOCAnimatedView.m所生成的目標文件中常侦。假如聲明此變量時不加static,則編譯器會為它創(chuàng)建一個"外部符號"(external symbol)贬媒。此時聋亡,若是另一個編譯單元中也聲明了同名變量,那么編譯器就拋出一條錯誤消息:

duplicate symbol _kAnimationDuration in:
EOCAnimatedView.o
EOCOtherView.o

實際上际乘,如果一個變量既聲明為static坡倔,又聲明為const,那么編譯器根本不會創(chuàng)建符號脖含,而是會像#define預處理指令一樣罪塔,把所有遇到的變量都替換為常值。不過還是要記住:用這種方式定義的常量帶有類型信息养葵。
有時候需要對外公開某個常量征堪。比方說,你可能要在類代碼中調用NSNotificationCenter以通知他人关拒。用一個對象來派發(fā)通知佃蚜,令其他欲接收通知的對象向該對象注冊庸娱,這樣就能實現此功能了。派發(fā)通知時爽锥,需要使用字符串來表示此項通知的名稱涌韩,而這個名字就可以聲明為一個外界可見的常值變量(constant variable)。這樣的話氯夷,注冊者無須知道實際字符串值臣樱,只需以常值變量來注冊自己想要接收的通知即可。
此類常量需放在"全局符號表"(global symbol table)中腮考,以便可以在定義該常量的編譯單元之外使用雇毫。因此,其定義方式與上例演示的static const有所不同踩蔚。應該這樣來定義:

//In the header file
extern NSString *const EOCStringConstant;

//In the implementation file
NSString *const EOCStringConstant = @"VALUE";

這個常量在頭文件中"聲明"棚放,且實現文件中"定義"。注意const修飾符在常量類型中的位置馅闽。常量定義應從右至左解讀飘蚯,所以在本例中,EOCStringConstant就是"一個常量福也,而這個常量是指針局骤,指向NSString對象"。這與需求相符:我們不希望有人改變此指針常量暴凑,使其指向另一個NSString對象峦甩。
編譯器看到頭文件中的extern關鍵字,就能明白如何在引入此頭文件的代碼中處理該常量了现喳。這個關鍵字是要告訴編譯器凯傲,在全局符號表中將會有一個名叫EOCStringConstant的符號嗦篱。也就是說灸促,編譯器無須查看其定義腿宰,即允許代碼使用此常量甩挫。因為它知道英遭,當鏈接成二進制文件之后挖诸,肯定能找到這個常量多律。
此類常量必須要定義帮碰,而且只能定義一次殉挽。通常將其定義在與聲明該常量的頭文件相關的實現文件里昔搂。由實現文件生成目標文件時贤斜,編譯器會在"數據段"(data section)為字符串分配存儲空間猴抹。鏈接器會把此目標文件與其他目標文件相鏈接阳堕,以生成最終的二進制文件恬总。凡是用到EOCStringConstant這個全局符號的地方拭卿,鏈接器都能將其解析峻厚。
因為符號要放在全局符號表里浦夷,所以命名常量時需謹慎。

總之摹恰,勿使用預處理指令定義常量姑宽,而應該借助編譯器來確保常量正確闺阱,比方說可以在實現文件中用static const來聲明常量炮车,也可以聲明一些全局常量。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末酣溃,一起剝皮案震驚了整個濱河市瘦穆,隨后出現的幾起案子,更是在濱河造成了極大的恐慌赊豌,老刑警劉巖扛或,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異碘饼,居然都是意外死亡熙兔,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門艾恼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來住涉,“玉大人,你說我怎么就攤上這事钠绍「殉常” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵五慈,是天一觀的道長纳寂。 經常有香客問我主穗,道長,這世上最難降的妖魔是什么毙芜? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任忽媒,我火速辦了婚禮,結果婚禮上腋粥,老公的妹妹穿的比我還像新娘晦雨。我一直安慰自己,他們只是感情好隘冲,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布闹瞧。 她就那樣靜靜地躺著,像睡著了一般展辞。 火紅的嫁衣襯著肌膚如雪奥邮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天罗珍,我揣著相機與錄音洽腺,去河邊找鬼。 笑死覆旱,一個胖子當著我的面吹牛蘸朋,可吹牛的內容都是我干的。 我是一名探鬼主播扣唱,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藕坯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了噪沙?” 一聲冷哼從身側響起炼彪,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎曲聂,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體佑惠,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡朋腋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了膜楷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片旭咽。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赌厅,靈堂內的尸體忽然破棺而出穷绵,到底是詐尸還是另有隱情,我是刑警寧澤特愿,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布仲墨,位于F島的核電站勾缭,受9級特大地震影響,放射性物質發(fā)生泄漏目养。R本人自食惡果不足惜俩由,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望癌蚁。 院中可真熱鬧幻梯,春花似錦、人聲如沸努释。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伐蒂。三九已至煞躬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饿自,已是汗流浹背汰翠。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昭雌,地道東北人复唤。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像烛卧,于是被迫代替她去往敵國和親佛纫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容