首頁 > 軟體

Objective-C const常數的優雅使用方法

2022-08-08 18:00:39

正文

在編寫程式碼時經常要使用常數,來替代 magic number。比較簡單的做法是通過預處理指令 #define 來實現。

#define ANIMATION_DURATION 0.3 

上述預處理指令會在編譯時的預處理階段會將程式碼中 ANIMATION_DURATION 字串替換為 0.3。這種定義常數的方式比較簡便,但是存在兩個問題:

  • 丟失了型別資訊。
  • 若該預處理指令宣告在標頭檔案中,引入該標頭檔案的程式碼,ANIMATION_DURATION 都會被替換,可能出現衝突。

Objective-C 的常數宣告方式

幸運的是,Objective-C 中提供了 const 關鍵字,可以用來定義常數。const 關鍵字可以對變數加以限定,使其值不能被改變,在整個作用域中都保持固定。

const NSTimeInterval kAnimationDuration = 0.3;

這種方式定義的常數包含型別資訊,且在編譯時即可檢查是否與其他常數出現衝突。如果試圖修改由 const 修飾符所宣告的變數,那麼編譯器就會報錯。

如果常數僅在某個實現檔案中使用,還應該加上 static 關鍵字,否則會被視為全域性常數。若不使用 static,編譯器會為它建立一個外部符號,若另一個編譯單元中也宣告了同名變數,就會報錯。

static const NSTimeInterval kAnimationDuration = 0.3;

當一個變數同時使用了 staticconst,那麼編譯器並不會建立符號,而是會像 #define 預處理指令一樣,把所有遇到的變數替換為常值。

有時候需要把一個常數暴露給外界使用,比如通知,此類常數需放在全域性符號表中。可以使用 extern 關鍵字,在標頭檔案中進行宣告:

// .h
extern NSString * const AFNetworkingTaskDidResumeNotification;
// .m
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";

該常數在標頭檔案中宣告,在實現檔案中定義。需要注意的是 const 寫在指標型別的右邊意味著該指標的指向不可被改變,若寫在左邊意味著該指標指向的內容不可被改變。

按上述方式實現並定義後,在編譯時生成目標檔案時,編譯器會在資料段為字串分配儲存空間。

Foundation 框架中,蘋果為了相容 C++ 中對 extern 的使用,提供了宏:

#if defined(__cplusplus)
#define FOUNDATION_EXTERN extern "C"
#else
#define FOUNDATION_EXTERN extern
#endif
#define FOUNDATION_EXPORT FOUNDATION_EXTERN
#define FOUNDATION_IMPORT FOUNDATION_EXTERN

一個 C++ 程式中可能包含其他語言編寫的部分程式碼,同樣,C++ 編寫的程式碼片段也可能被用在其他語言編寫的程式碼中。但是,不同語言編寫的程式碼相互呼叫是困難的,更何況用同一種語言編寫,使用不同編譯器進行編譯的情況。因為,不同語言或者同種語言在不同編譯器上編譯時,在註冊變數,傳遞引數和引數在棧上的佈局上可能存在差異。

為了使它們遵守統一規則,可以使用 extern 指定一個編譯和連結規約。extern "C" 指令中的 C,表示的是一種編譯和連結規約,而不是一種語言。C 表示符合 C 語言的編譯和連結規約的任何語言。

還要說明的是,extern "C" 指令指定的編譯和連結規約,不會影響語意,只是改變編譯和連結的方式。

FOUNDATION_EXPORTFOUNDATION_IMPORT 是用來相容 Win32 應用程式的,行動端開發可以忽略。

所以上述對全域性常數的宣告,可以寫成:

// .h
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;
// .m
NSString * const AFNetworkingTaskDidResumeNotification = @"com.alamofire.networking.task.resume";

在 Objective-C 中使用 let 來宣告常數

使用過 Swift 的同學,一定對其宣告常數的方式的簡潔性印象深刻,在 Swift 中宣告常數的方式如下所示:

let kAnimationDuration = 0.3

之所以能如此簡潔,是因為 Swift 具有 let 關鍵字和型別推斷的能力,但其實在 Objective-C 中也可以通過類似的方式來書寫常數。

Objective-C 中有一個關鍵字,是 __auto_type,可以實現類似 Swift 中型別推斷能力的關鍵字,如下所示:

const __auto_type kAnimationDuration = 0.3;

可能對於簡單的資料型別,這樣的優勢不是很明顯,但是對於具有複雜泛型的型別來說,可以說優勢很大了:

// 舊方式
NSArray<NSDictionary<NSString *, NSString *> *> *models = ...;
// 新方式
__auto_type models = ...;

同時,可以通過宏的方式,來減少 __auto_type 的書寫,即可實現通過 let 宣告常數,var 宣告變數。其中 auto 關鍵字是為了相容 C++。

#if defined(__cplusplus)
#define let auto const
#else
#define let const __auto_type
#endif
#if defined(__cplusplus)
#define var auto
#else
#define var __auto_type
#endif

宣告了上面的宏之後,就可以直接使用了:

let kAnimationDuration = 0.3;

以上就是Objective-C const常數的優雅使用方法的詳細內容,更多關於Objective-C const常數的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com