多用型別常量,少用 define預處理指令

2021-07-11 23:03:49 字數 4431 閱讀 9381

如何使用

只在乙個檔案中使用

static  

const  

nstimeinterval

kanimaltime =

0.3;

多個類中使用,全域性

extern 

nstimeinterval  

const

kanimaltime; //in the header file

const  

nstimeinterval 

kanimaltime =

0.3; //in the implementation file

#define animation_duration 0.3 

上述預處理指令會把源**中的animation_duration字串替換為0.3。這可能就是你想要的效果,不過這樣定義出來的常量沒有型別資訊。「持續」(duration)這個詞看上去應該與時間有關,但是**中又未明確指出。此外,預處理過程會把碰到的所有animation_duration一律替換成0.3,這樣的話,假設此指令宣告在某個標頭檔案中,那麼所有引入了這個標頭檔案的**,其animation_duration都會被替換。

要想解決此問題,應該設法利用編譯器的某些特性才對。有個辦法比用預處理指令來定義常量更好。比方說,下面這行**就定義了乙個型別為nstimeinterval的常量:

static const nstimeinterval 

kanimationduration= 0

.3; 

請注意,用此方式定義的常量包含型別資訊,其好處是清楚地描述了常量的含義。由此可知該常量型別為nstimeinterval,這有助於為其編寫開發文件。如果要定義許多常量,那麼這種方式能令稍後閱讀**的人更易理解其意圖。

還要注意常量名稱。常用的命名法是:若常量侷限於某「編譯單元」(translation unit,也就是「實現檔案」,implementation file)之內,則在前面加字母k;若常量在類之外可見,則通常以類名為字首。第19條詳解了命名習慣(naming convention)。

定義常量的位置很重要。我們總喜歡在標頭檔案裡宣告預處理指令,這樣做真的很糟糕,當常量名稱有可能互相衝突時更是如此。例如,animation_duration這個常量名就不該用在標頭檔案中,因為所有引入了這份標頭檔案的其他檔案中都會出現這個名字。其實就連用static const定義的那個常量也不應出現在標頭檔案裡。因為objective-c沒有「命名空間」(namespace)這一概念,所以那樣做等於宣告了乙個名叫kanimationduration的全域性變數。此名稱應該加上字首,以表明其所屬的類,例如可改為eocviewclassanimationduration。本書第19條中深入講解了一套清晰的命名方案。

// eocanimatedview.h  

#import 

<

uikit

/uikit.h

>

@inte***ce eocanimatedview : uiview  

- (void)animate;  

@end  

// eocanimatedview.m  

#import "eocanimatedview.h"  

static const nstimeinterval 

kanimationduration= 0

.3;  

@implementation eocanimatedview  

- (void)animate ];  

}  @end 

duplicate symbol _kanimationduration in:  

eocanimatedview.o  

eocotherview.o 

實際上,如果乙個變數既宣告為static,又宣告為const,那麼編譯器根本不會建立符號,而是會像#define預處理指令一樣,把所有遇到的變數都替換為常值。不過還是要記住:用這種方式定義的常量帶有型別資訊。

有時候需要對外公開某個常量。比方說,你可能要在類**中呼叫nsnotificationcenter以通知他人。用乙個物件來派發通知,令其他欲接收通知的物件向該物件註冊,這樣就能實現此功能了。派發通知時,需要使用字串來表示此項通知的名稱,而這個名字就可以宣告為乙個外界可見的常值變數(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這個全域性符號的地方,鏈結器都能將其解析。

因為符號要放在全域性符號表裡,所以命名常量時需謹慎。例如,某應用程式中有個處理登入操作的類,在登入完成後會發出通知。派發通知所用的**如下:

// eocloginmanager.h  

#import 

<

foundation

/foundation.h

>

extern nsstring *const eocloginmanagerdidloginnotification;  

@inte***ce eocloginmanager : nsobject  

- (void)login;  

@end  

// eocloginmanager.m  

#import "eocloginmanager.h"  

nsstring *const 

eocloginmanagerdidloginnotification

=  @"eocloginmanagerdidloginnotification";  

@implementation eocloginmanager  

- (void)login   

- (void)p_didlogin   

@end 

// eocanimatedview.h  

extern const nstimeinterval eocanimatedviewanimationduration;  

// eocanimatedview.m  

const nstimeinterval 

eocanimatedviewanimationduration= 0

.3; 

這樣定義常量要優於使用#define預處理指令,因為編譯器會確保常量值不變。一旦在eocanimatedview.m中定義好,即可隨處使用。而採用預處理指令所定義的常量可能會無意中遭人修改,從而導致應用程式各個部分所使用的值互不相同。

總之,勿使用預處理指令定義常量,而應該借助編譯器來確保常量正確,比方說可以在實現檔案中用static const來宣告常量,也可以宣告一些全域性常量。

要點不要用預處理指令定義常量。這樣定義出來的常量不含型別資訊,編譯器只是會在編譯前據此執行查詢與替換操作。即使有人重新定義了常量值,編譯器也不會產生警告資訊,這將導致應用程式中的常量值不一致。

在實現檔案中使用static const來定義「只在編譯單元內可見的常量」(translation-unit-specific constant)。由於此類常量不在全域性符號表中,所以無須為其名稱加字首。

在標頭檔案中使用extern來宣告全域性常量,並在相關實現檔案中定義其值。這種常量要出現在全域性符號表中,所以其名稱應加以區隔,通常用與之相關的類名做字首。

參考:《effective objective-c 2.0:編寫高質量ios與os x**的52個有效方法》書籍

4 多用型別常量,少用 define預處理命令

define animation duration 0.3 缺點 1 這樣定義沒有型別資訊,使閱讀 的人難以理解其意圖。2 假設此指令宣告在了某個標頭檔案中,那麼所有引入這個標頭檔案的 其animation duration都會背替換為0.3。更好的定義方式 static const nstimei...

04 多用型別常量,少用 define預處理指令

預處理指令,預處理指令把原始碼中animation defation替換為0.3 define animation defation 0.3 不推薦 推薦 定義常量的方法 static const nstimeinterval kanimationduration 0.3 變數一定要同時用stati...

程式設計 多用組合少用繼承原則

一 方法服用的兩種最常用的技術就是類繼承和物件組合。1.繼承復用 子類中可以將父類的方法當作自己的方法使用 非private修飾的 優點 可以通過重寫父類方法,來修改或擴充套件父類方法 缺點 繼承來的方法在編譯期就已經確定,無法在執行期間改變從父類繼承來的方法的行為 子類和父類是強耦合關係,也就是說...