常量
- 程序中的某些信息的值是永遠不會變的惨远,這類不變的值稱為常量(constant)。
- 在 Objective-C 中话肖,可以通過兩種途徑來定義 常量:
- 全局變量锨络;
-
#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";
extern:NSLocaleCountryCode 是存在的悴势,但是會在另一個文件中定義。(即在.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
矗蕊、unsigned
、long
等)氢架。
如果使用舊的語法傻咖,編輯器會為 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
};