《Objective-C 編程》25.常量、#define朽缎、enum

常量

  • 程序中的某些信息的值是永遠不會變的惨远,這類不變的值稱為常量(constant)
  • 在 Objective-C 中话肖,可以通過兩種途徑來定義 常量
    1. 全局變量锨络;
    2. #define 預處理程序;
  • 常量應該以駝峰法命名狼牺,并以相關類名作為前綴羡儿。

??????最佳實踐:若常量僅局限于實現(xiàn)文件(.m)之內(nèi),則在前面加字母 K是钥。若常量在類之外可見掠归,則通常以類名為前綴。

static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4;
static NSString * const ZOCCacheControllerDidClearCacheNotification = @"ZOCCacheControllerDidClearCacheNotification";
static const CGFloat ZOCImageThumbnailHeight = 50.0f;
  • 常量應該在頭文件中以這樣的形式暴露給外部:
extern NSString *const ZOCCacheControllerDidClearCacheNotification;

// 示例
// .h
UIKIT_EXTERN NSString *const HQFormRowDescriptorTypeProvinceAndCity;
UIKIT_EXTERN const CGFloat HQLTableViewCellHeight;
// .m
NSString *const HQFormRowDescriptorTypeProvinceAndCity = @"HQProvinceAndCityCell";
const CGFloat HQLTableViewCellHeight = 115.0f;

并在實現(xiàn)文件中為它賦值悄泥。
只有公有的常量才需要添加命名空間作為前綴虏冻。盡管實現(xiàn)文件中私有常量的命名可以遵循另外一種模式,你仍舊可以遵循這個規(guī)則弹囚。

預處理指令

C厨相、C++ 或 Objective-C 代碼文件要經(jīng)過兩步才能完成編譯。首先,預處理器(preprocessor)會讀入并處理整個文件蛮穿。接著庶骄,預處理器的輸出結(jié)果會作為輸入交給真正的編譯器預處理器指令都以 # 開頭践磅。其中最常用的三個為 #include单刁、#import#define

#include 與 #import

  • #include#import 的作用類似府适,都是先要求預處理器讀取某個文件羔飞,然后將讀入的內(nèi)容添加至輸出結(jié)果。
  • 區(qū)別:#import 會確保預處理器只導入特定的文件一次檐春,而 #include 則允許多次導入同一個文件逻淌。
#import <<#header#>>
#import "<#header#>"
  • < >尖括號的文件,表示編譯器會先在預先設定好的標準目錄下查找相應的頭文件疟暖。
  • " "雙引號的文件卡儒,表示編譯器會先在項目目錄下查找相應的頭文件。
  • 凡是出現(xiàn)在預編譯文件(.pch)中的頭文件誓篱,Xcode 都會事前編譯好并自動導入每個文件

#define

#define 告訴預處理器凯楔,在編譯器看到A之前窜骄,使用B替換之。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // M_PI 是在 <math.h> 中定義的常量
        NSLog(@"\u03c0 is %f",M_PI);  
    }
    return 0;
}

M_PI 是在 math.h 中定義的常量:

#define M_PI        3.14159265358979323846264338327950288   /* pi             */

??????在定義常量時摆屯,你應該使用全局變量和 enum邻遏,而不是 #define

除了定義常量虐骑,還可以使用 #define 構(gòu)建類似函數(shù)的代碼段准验,稱之為 宏(macro)

NSLog(@"%d is larger",MAX(10, 12));

這里的 MAX 不是函數(shù)廷没,而是一個 #define 指令:

#if !defined(MAX)
    #define __NSMAX_IMPL__(A,B,L) ({ __typeof__(A) __NSX_PASTE__(__a,L) = (A); __typeof__(B) __NSX_PASTE__(__b,L) = (B); (__NSX_PASTE__(__a,L) < __NSX_PASTE__(__b,L)) ? __NSX_PASTE__(__b,L) : __NSX_PASTE__(__a,L); })
    #define MAX(A,B) __NSMAX_IMPL__(A,B,__COUNTER__)
#endif

宏定義的日常使用:

  • 宏定義顏色
// 獲取RGB顏色
#define Color_A(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)]
#define Color(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]

#define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define RGB(r,g,b) RGBA(r,g,b,1.0f)

// rgb顏色轉(zhuǎn)換(16進制->10進制)糊饱,輸入十六進值顏色值@“#000000”
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

// 隨機色
#define RANDOM_COLOR [UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:arc4random_uniform(255)/255.0]

// 背景色
#define COLOR_BACKGROUND [UIColor colorWithRed:239/255.0 green:239/255.0 blue:239/255.0 alpha:1.0]

// Chameleon 框架中的宏定義語法: https://github.com/ViccAlexander/Chameleon
#define rgba(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
#define rgb(r,g,b) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:1.0]
#define hsba(h,s,b,a) [UIColor colorWithHue:h/360.0f saturation:s/100.0f brightness:b/100.0f alpha:a]
#define hsb(h,s,b) [UIColor colorWithHue:h/360.0f saturation:s/100.0f brightness:b/100.0f alpha:1.0]
  • 宏定義字體
#define CYLabelFont [UIFont boldSystemFontOfSize:13]
#define CYFont(f) [UIFont systemFontOfSize:(f)]
#define CYFontB(f) [UIFont boldSystemFontOfSize:(f)]
  • 屏幕的寬高
#define CYScreen [UIScreen mainScreen]
#define CYScreenWidth CYScreen.bounds.size.width
#define CYScreenHeight CYScreen.bounds.size.height

補充,如果你使用 YYKit 框架颠黎,那么 YYCGUtilities.h 中已經(jīng)寫好了:

// main screen's scale
#ifndef kScreenScale
#define kScreenScale YYScreenScale()
#endif

// main screen's size (portrait)
#ifndef kScreenSize
#define kScreenSize YYScreenSize()
#endif

// main screen's width (portrait)
#ifndef kScreenWidth
#define kScreenWidth YYScreenSize().width
#endif

// main screen's height (portrait)
#ifndef kScreenHeight
#define kScreenHeight YYScreenSize().height
#endif
  • 弧度與角度的相互轉(zhuǎn)換
/** 角度轉(zhuǎn)換為弧度 */
#define DEGREES_TO_RADIANS(degrees) ((degrees) * M_PI / 180)
/** 弧度轉(zhuǎn)換為角度 */
#define RADIANS_TO_DEGREES(radians) ((radians) * 180 / M_PI)

補充另锋,如果你使用 YYKit 框架,那么 YYCGUtilities.h 中已經(jīng)寫好了:

/// Convert degrees to radians.
static inline CGFloat DegreesToRadians(CGFloat degrees) {
    return degrees * M_PI / 180;
}

/// Convert radians to degrees.
static inline CGFloat RadiansToDegrees(CGFloat radians) {
    return radians * 180 / M_PI;
}
  • 強引用與弱引用
#define WS(weakself)  __weak __typeof(&*self)weakself = self;
#define SS(strongself)  __strong __typeof(&*self)strongself = self;

#define WeakSelf(weakSelf)      __weak __typeof(self)weakSelf = self;
#define StrongSelf(strongSelf)  __strong __typeof(weakSelf)strongSelf = weakSelf;

補充狭归,如果你使用 YYKit 框架夭坪,那么 YYCategoriesMacro.h 中已經(jīng)寫好了:

#ifndef weakify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
        #else
        #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
        #endif
    #endif
#endif

#ifndef strongify
    #if DEBUG
        #if __has_feature(objc_arc)
        #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
        #endif
    #else
        #if __has_feature(objc_arc)
        #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
        #else
        #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
        #endif
    #endif
#endif
  • 獲取沙盒目錄
#define PATH_TEMP  NSTemporaryDirectory()
#define PATH_DOCUMENT  [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
#define PATH_CACHE  [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];

補充,如果你使用 YYKit 框架过椎,那么UIApplication+YYAdd 中已經(jīng)寫成了屬性的形式:

/// "Documents" folder in this app's sandbox.
@property (nonatomic, readonly) NSURL *documentsURL;
@property (nonatomic, readonly) NSString *documentsPath;

/// "Caches" folder in this app's sandbox.
@property (nonatomic, readonly) NSURL *cachesURL;
@property (nonatomic, readonly) NSString *cachesPath;

/// "Library" folder in this app's sandbox.
@property (nonatomic, readonly) NSURL *libraryURL;
@property (nonatomic, readonly) NSString *libraryPath;

實現(xiàn)的方式是一模一樣的:

- (NSURL *)documentsURL {
    return [[[NSFileManager defaultManager]
             URLsForDirectory:NSDocumentDirectory
             inDomains:NSUserDomainMask] lastObject];
}

- (NSString *)documentsPath {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
}

- (NSURL *)cachesURL {
    return [[[NSFileManager defaultManager]
             URLsForDirectory:NSCachesDirectory
             inDomains:NSUserDomainMask] lastObject];
}

- (NSString *)cachesPath {
    return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
}

- (NSURL *)libraryURL {
    return [[[NSFileManager defaultManager]
             URLsForDirectory:NSLibraryDirectory
             inDomains:NSUserDomainMask] lastObject];
}

- (NSString *)libraryPath {
    return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
}
  • View 設置圓角邊框
#define ViewBorderRadius(View, Radius, Width, Color)  \
[View.layer setCornerRadius:(Radius)];                \
[View.layer setMasksToBounds:YES];                    \
[View.layer setBorderWidth:(Width)];                  \
[View.layer setBorderColor:[Color CGColor]]
  • 宏定義指定字符串
#define URL     @"https://www.google.com"
  • 圖片路徑
#define CYPasswordViewSrcName(file) [@"CYPasswordView.bundle" stringByAppendingPathComponent:file]
  • 判斷系統(tǒng)版本(來自 XLForm.h
#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

// 使用示例室梅,傳入版本號字符串即可
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")){
    self.tableView.rowHeight = UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight = 44.0;
}

全局變量

  • 局部變量:程序只在函數(shù)運行時才會存在的變量;
  • 全局變量:任何函數(shù)都可以在任意時刻訪問的變量;
  • 靜態(tài)變量: 只有聲明某個靜態(tài)變量的文件才能訪問該變量,靜態(tài)變量既保留了非局部的亡鼠,存在于任何函數(shù)之外的優(yōu)點赏殃,又避免了會被其他文件修改的問題。
  • 如果沒有為靜態(tài)變量全局變量賦初值拆宛,則程序會將他們的值自動賦為0.

編寫 Objective-C 程序時嗓奢,通常不是使用 #define 來保存 常量,而是使用 全局變量 來完成這項任務浑厚。因為在某些情況下股耽,使用全局變量的效率更高

// 獲取用戶當前區(qū)域的貨幣
NSLocale *here = [NSLocale currentLocale];
//  NSString *currency = [here objectForKey:@"currency"];
// 使用全局變量替換
NSString *currency = [here objectForKey:NSLocaleCountryCode];
NSLog(@"Money is %@",currency);

NSLocaleCountryCode 是一個全局變量钳幅,它的聲明如下:

FOUNDATION_EXPORT NSLocaleKey const NSLocaleCountryCode;        // NSString

關鍵字

  • const :在程序的整個運行過程中物蝙,NSLocaleCountryCode 指針的值不會發(fā)生變化。

    ?? 難點 ??
    // ********************************************************
    const CGFloat count = 6;
    const NSString *string = @"string";
    // const放在最前面可以往后移一位,含義不變,因此下面兩個寫法和上面的等價
    CGFloat const count = 6;
    NSString const *string = @"string";
    
    // ********************************************************
    // age 是【指針變量】, const 修飾指針變量,意味著【這個指針變量】不可修改,而指針變量存的是地址,意思就是不能把 【這個指針變量存的地址】 改成 【其他地址】.或者說不能改變指針的指向敢艰。
    int * const age = 25;
    
    // age 是【指針變量】, *age 是取得【指針所指向的值】, const 修飾 *age, 意味著 【*age 取得指針所指向的值】 不可修改诬乞。
    int const *age = 23;
    
    
    // ********************************************************
    // p  : 指針變量
    // *p : 取指針指向的內(nèi)存
    // &p : 取指針本身的地址
    // const修飾【指針變量訪問的內(nèi)存空間】,修飾的是右邊*p1钠导,
    // 兩種方式一樣
    const int *p1; // *p1:常量 p1:變量
    int const *p1; // *p1:常量 p1:變量
    
    // const修飾指針變量p1
    int * const p1; // *p1:變量 p1:常量
    
    
    // 第一個const修飾*p1震嫉,第二個const修飾 p1 (技巧:從后向前看)
    // 兩種方式一樣
    const int * const p1;  // *p1:常量 p1:常量
    int const * const p1;  // *p1:常量 p1:常量
    
    // 開發(fā)中經(jīng)常拿到key修改值,因此用const修飾key,表示key只讀牡属,不允許修改票堵。
    static  NSString * const key = @"name";
    
    // 如果 const修飾 *key1,表示*key1只讀,key1還是能改變逮栅。
    static  NSString const *key1 = @"name";
    
  • externNSLocaleCountryCode 是存在的悴势,但是會在另一個文件中定義。(即在.h文件中聲明措伐,在.m文件中賦值)

  • UIKIT_EXTERN: 是蘋果使用的 extern ,只是添加了一些編譯器代碼特纤,是經(jīng)過處理的 extern

  • static : 該變量僅在定義此變量的編譯單元中可見侥加。

enum 枚舉類型

枚舉類型用于定義一組常量捧存。

  • 使用 enum 枚舉類型
typedef enum : NSUInteger {
    MyEnumValueA,
    MyEnumValueB,
    MyEnumValueC,
} MyEnum;
  • 使用 NS_ENUM(…) 語法
typedef NS_ENUM(NSUInteger, BlenderSpeed) {
    BlenderSpeedStir = 1,
    BlenderSpeedChop = 2,
    BlenderSpeedLiquify = 3,
    BlenderSpeedPulse = 4,
    BlenderSpeedIceCrush = 5
};

@property (nonatomic, assign) BlenderSpeed mySpeed;

NS_ENUM(…) 實際上是一個預處理宏,它帶有兩個實參:數(shù)據(jù)類型和名字担败。使用 NS_ENUM(…) 的優(yōu)點是它可以聲明整數(shù)數(shù)據(jù)類型(short矗蕊、unsignedlong 等)氢架。

如果使用舊的語法傻咖,編輯器會為 enum 選擇合適的數(shù)據(jù)類型,通常是 int 類型岖研。如果你的 enum 只需要枚舉四個常量卿操,那么它的值是什么都無所謂警检,就不需要四個字節(jié)來存儲它。一個字節(jié)就可以存儲到 255 的整數(shù)害淤。比如 char 類型就是一個一個字節(jié)的整數(shù)扇雕,因此,我們可以聲明一個節(jié)省內(nèi)存的 enum

typedef NS_ENUM(char, BlenderSpeed) {
    BlenderSpeedStir,
    BlenderSpeedChop,
    BlenderSpeedLiquify,
    BlenderSpeedPulse,
    BlenderSpeedIceCrush
};

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窥摄,一起剝皮案震驚了整個濱河市镶奉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崭放,老刑警劉巖哨苛,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異币砂,居然都是意外死亡建峭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門决摧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亿蒸,“玉大人,你說我怎么就攤上這事掌桩”咚” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵波岛,是天一觀的道長茅坛。 經(jīng)常有香客問我,道長盆色,這世上最難降的妖魔是什么灰蛙? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任祟剔,我火速辦了婚禮隔躲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘物延。我一直安慰自己宣旱,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布叛薯。 她就那樣靜靜地躺著浑吟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耗溜。 梳的紋絲不亂的頭發(fā)上组力,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音抖拴,去河邊找鬼燎字。 笑死腥椒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的候衍。 我是一名探鬼主播笼蛛,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛉鹿!你這毒婦竟也來了滨砍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤妖异,失蹤者是張志新(化名)和其女友劉穎惋戏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體随闺,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡日川,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了矩乐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片龄句。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖散罕,靈堂內(nèi)的尸體忽然破棺而出分歇,到底是詐尸還是另有隱情,我是刑警寧澤欧漱,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布职抡,位于F島的核電站,受9級特大地震影響误甚,放射性物質(zhì)發(fā)生泄漏缚甩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一窑邦、第九天 我趴在偏房一處隱蔽的房頂上張望擅威。 院中可真熱鬧,春花似錦冈钦、人聲如沸郊丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厉熟。三九已至,卻和暖如春较幌,著一層夾襖步出監(jiān)牢的瞬間揍瑟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工乍炉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绢片,地道東北人嘁字。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像杉畜,于是被迫代替她去往敵國和親纪蜒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容

  • static 修飾局部變量 讓局部變量只初始化一次 局部變量在程序中只有一份內(nèi)存 并不會改變局部變量的作用域此叠,僅僅...
    大沖哥閱讀 1,345評論 0 1
  • 注:這是第三遍讀《C語言深度解剖》纯续,想想好像自從大學開始就沒讀完過幾本書,其中譚浩強的那本《C語言程序設計(第四版...
    HavenXie閱讀 1,717評論 1 6
  • 1灭袁、@synthesize @dynamic 的區(qū)別 @property有兩個對應的詞,@synthsize @d...
    Bruce_XHG閱讀 635評論 0 2
  • 為了早日擺脫某種心理狀態(tài)猬错,我決定中午下班后留在辦公室半個小時。 于是吃完飯茸歧,午休的時間變得很少倦炒。我以前看過一句話并...
    桃釀釀閱讀 158評論 0 0
  • 天響一聲悶雷 地生一雙重瞳 你的出生便預示著歷史的改寫 力能扛鼎,氣度不凡 注定你與別人不同 眾人中一句彼可取而代...
    鶑鵅閱讀 233評論 0 2