前言
宏定義在C系開發中可以說占有舉足輕重的作用。底層框架自不必說,為了編譯優化和方便,以及跨平臺能力,宏被大量使用,可以說底層開發離開define將寸步難行。而在更高層級進行開發時,我們會將更多的重心放在業務邏輯上,似乎對宏的使用和依賴并不多。
編譯時編譯器會在語義分析認定是宏后,將形參替換為實參,這個過程稱為宏的展開。
使用宏的好處:
- 在節省工作量的同時,代碼可讀性大大增加。如打印語句可以使打印出來的內容更美觀。
- 寫出漂亮優雅的代碼(雖然宏本身可能并不漂亮優雅)。
- 我們使用宏一是為了多處使用方便,而是方便修改:一處修改全局適用。
宏的分類使用
一般宏分為兩類
對象宏(object-like macro)和函數宏(function-like macro)。
- 對象宏:對于對象宏來說確實相對簡單,一般用來定義一些常數。
- 函數宏:函數宏顧名思義,就是行為類似函數,可以接受參數的宏。
在書寫函數宏的時候注意多加(),這樣可以避免優先級所造成的問題
一般開發中使用的宏大體為:
- 顏色
- 服務器地址
- 儲存取值
- 屏幕寬高
- 導航高度
- 縮放比例
- 其他常用的代碼段
常用的宏定義(不全,但是以此類推)
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
#define NavBarHeight 64.0f
#define TabBarHeight 49.0f
#define selfWidth self.frame.size.width
#define selfHeight self.frame.size.height
#define ViewBorderRadius(View,Radius,Width,Color) [View.layer setCornerRadius:(Radius)];\
[View.layer setMasksToBounds:YES];\
[View.layer setBorderWidth:(Width)];\
[View.layer setBorderColor:[Color CGColor]];//設置圓角
weak弱引用self
#define WeakSelf __weak typeof(self) weakSelf = self;
weak弱引用對象
#define WeakObj(obj) __weak typeof(obj) weakObj = obj;
strong強引用self
#define StrongSelf(weakSelf) __strong typeof(weakSelf) strongSelf = weakSelf;
strong強引用對象
#define StrongObj(obj) @autoreleasepool{} __strong typeof(obj) obj = strongObj;
#define IS_IOS8 ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8)
//判斷是不是iPad
#define IS_iPad UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
//判斷是不是iphone
#define IS_IPHONE [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone
#define GetImage(imageName) [UIImage imageNamed:[NSString stringWithFormat:@"%@",imageName]]
#define NEWSTITLECOLOR ([UIColor colorWithRed:64/255.0 green:56/255.0 blue:53/255.0 alpha:1.0f])
#define showMessage(a) [SVProgressHUD showInfoWithStatus:a maskType:SVProgressHUDMaskTypeGradient];
#define showSuccessMessage(a) [SVProgressHUD showSuccessWithStatus:a maskType:SVProgressHUDMaskTypeGradient];
#define SandboxPathStr [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
#define cafPathStr [SandboxPathStr stringByAppendingPathComponent:@"myRecord.caf"]
#define mp3PathStr [SandboxPathStr stringByAppendingPathComponent:@"myRecord.mp3"]
字符串是否為空
#define LMStringIsEmpty(str) ([str isKindOfClass:[NSNull class]] || str == nil || [str length] < 1 ? YES : NO )
數組是否為空
#define LMArrayIsEmpty(array) (array == nil || [array isKindOfClass:[NSNull class]] || array.count == 0)
字典是否為空
#define LMDictIsEmpty(dic) (dic == nil || [dic isKindOfClass:[NSNull class]] || dic.allKeys == 0)
#define LMApplication [UIApplication sharedApplication]
#define LMKeyWindow [UIApplication sharedApplication].keyWindow
#define LMAppVersion [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]
property屬性快速聲明
#define PropertyString(s) @property(nonatomic,copy)NSString * s
#define PropertyNSInteger(s) @property(nonatomic,assign)NSInteger s
#define PropertyFloat(s) @property(nonatomic,assign)float s
#define propertyStrong(a,b) @property (strong, nonatomic) a *b;
DEBUG模式下打印日志,當前行
#ifdef DEBUG
#define DLog(fmt,...)NSLog((@"%s[Line %d]" fmt),__PRETTY_FUNCTION__,__LINE__,##__VA_ARGS__);
#else
#define DLog(...)
#endif
傳參view對象,獲取view的frame、bounds相關屬性值
#define VIEW_BOUNDS(aView) ((aView).bounds)
#define VIEW_FRAME(aView) ((aView).frame)
#define VIEW_ORIGIN(aView) ((aView).frame.origin)
#define VIEW_X(aView) ((aView).frame.origin.x)
#define VIEW_Y(aView) ((aView).frame.origin.y)
#define VIEW_SIZE(aView) ((aView).frame.size)
#define VIEW_HEIGHT(aView) ((aView).frame.size.height) // 視圖高度
#define VIEW_WIDTH(aView) ((aView).frame.size.width) // 視圖寬度
獲取iPhone屏幕尺寸
// x
#define IS_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO)
// 6P、6sP、7P、8P
#define IS_iPhone678_Plus ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2208), [[UIScreen mainScreen] currentMode].size) : NO)
// 6、6s、7、8
#define IS_iPhone678 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(750, 1334), [[UIScreen mainScreen] currentMode].size) : NO)
// 5、5s
#define IS_iPhone5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(640, 1136), [[UIScreen mainScreen] currentMode].size) : NO)
// 4、4s
#define IS_iPhone4 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(640, 960), [[UIScreen mainScreen] currentMode].size) : NO)
大小屏字體自動切換
有的應用希望有一個好的用戶體驗會在不同的屏幕上適配不同大小字體,這時就可以使用以下的宏定義來實現。但是如果應用中字體大小不能做到全局統一,就不要使用以下的宏定義來實現字體大小適配。例如:
#define IS_SmallScreen (IS_iPhone5 || IS_iPhone4)
#define MaxFontSize (IS_SmallScreen ? 21.f : 25.f )
#define LagerFontSize (IS_SmallScreen ? 17.f : 19.f )
#define BigFontSize (IS_SmallScreen ? 15.f : 17.f )
#define NormalFontSize (IS_SmallScreen ? 13.f : 15.f )
#define SmallFontSize (IS_SmallScreen ? 11.f : 13.f )
#define MinFontSize (IS_SmallScreen ? 9.f : 11.f )
校驗相關
#define IsCanUseString(str) ((str != nil) && ![str isKindOfClass:[NSNull class]] && [str isKindOfClass:[NSString class]] && [str length] > 0 )
#define IsCanUseArray(arr) ( arr && (arr != nil) && ![arr isKindOfClass:[NSNull class]] )
#define IsCanUseDic(dic) ( dic && (dic != nil) && ![dic isKindOfClass:[NSNull class]] )
#define IsCanUseObj(obj) ( obj && (obj != nil) && ![obj isKindOfClass:[NSNull class]] )
#define IsNullClass(class) [class isKindOfClass:[NSNull class]]
打印相關
這樣的打印語句,省事而且美觀易讀
// mark(NSString類型參數)為打印內容標題
#define NSLOG_Str(mark,str) NSLog(@"##%@##--str:%@--",(mark),(str))
#define NSLOG_Int(mark,int) NSLog(@"##%@##--int:%ld--",(mark),(int))
#define NSLOG_Float(mark,float) NSLog(@"##%@##--float:%f--",(mark),(float))
#define NSLOG_Bool(mark,bool) NSLog(@"##%@##--bool:%@--",(mark),(bool) ? @"YES" : @"NO")
#define NSLOG_Point(mark,point) NSLog(@"##%@##--x:%f--y:%f--",(mark),(point).x,(point).y)
#define NSLOG_Size(mark,size) NSLog(@"##%@##--width:%f--height:%f--",(mark),(size).width,(size).height)
#define NSLOG_Frame(mark,frame) NSLog(@"##%@##--x:%f--y:%f--width:%f--height:%f--",(mark),(frame).origin.x,(frame).origin.y,(frame).size.width,(frame).size.height)
條件編譯
一般情況下,源程序中所有的行都參加編譯。但是有時希望對其中一部分內容只在滿足一定條件才進行編譯,也就是對一部分內容指定編譯的條件,這就是條件編譯(不被編譯的代碼不會被運行)
條件編譯語法格式
1、#if 編譯預處理中的條件命令, 相當于C語法中的if語句
2、#ifdef 判斷某個宏是否被定義, 若已定義, 執行隨后的語句
3、#ifndef 與#ifdef相反, 判斷某個宏是否未被定義
4、#elif 若#if, #ifdef, #ifndef或前面的#elif條件不滿足, 則執行#elif之后的語句, 相當于C語法中的else-if
5、#else 與#if, #ifdef, #ifndef對應, 若這些條件不滿足, 則執行#else之后的語句, 相當于C語法中的else
6、#endif #if, #ifdef, #ifndef這些條件命令的結束標志.
7、#if 與 #ifdef 的區別:#if是判斷后面的條件語句是否成立,#ifdef是判斷某個宏是否被定義過。要區分開!
#if 條件1
...code1...
#elif 條件2
...code2...
#else
...code3...
#endif
*******************
#ifdef 標識符 // 如果定義了 標識符
程序段1
#else
程序段2
#endif
它的作用是:當標識符已經被定義過(一般是用#define命令定義),則對程序段1進行編譯,否則編譯程序段2。其中#else部分也可以沒有,即:
#ifdef
程序段1
#denif
*******************
#ifndef MACRO_Define // 如果未定義MACRO_Define這個宏
代碼塊1
#else
代碼塊2
#endif
說明:預處理指令是編譯之前的,不是運行時的,所以條件編譯時要注意if的條件,不要還沒運行,就先用源程序里面的變量作為條件進行判斷,變量是運行時才產生的,而條件編譯呢是在運行之前編譯的。所以條件編譯的條件一般是利用宏定義,因為宏定義和條件編譯都是編譯之前進行的。
如下面的一個錯誤例子:
#include<stdio.h>
void main()
{
int a =8;
#if a>7
printf("a>7");
#elif a<7
printf("a<7");
#else
printf("a!=7");
#endif
}
運行結果為a<7,與我們期望的結果不一樣,正常應該是輸出a>7才對。
正確的寫法如下:
#include<stdio.h>
#define a 8
void main()
{
#if a>7
printf("a>7");
#elif a<7
printf("a<7,%d",a);
#else
printf("a!=7");
#endif
}
輸出結果為a>7
條件編譯的使用
測試服務器、正式服務器的自動切換。
#ifdef DEBUG
#define request11 @"123"
#else
#define request11 @"4565"
#endif
如何設置環境變量配置進行條件編譯
-
通過 Configurations 添加多個環境
-
不同環境下設置不同的宏定義(在某個環境下設置的宏只能在哪個環境下的 Targets 中被識別,否則會報錯,所以建議不同環境下定義同一個全局變量為不同的值。)
-
區分不同的環境
if (DEBUG==1) { NSLog(@"測試環境"); }else if (DEBUG==2){ NSLog(@"運營環境"); }else{ NSLog(@"生產環境"); }
我們可以通過設置多個環境,每一種環境下的值不同;
生成多個Scheme,每一個Scheme對應一種環境和配置;
這樣切換Scheme 即可切換到不同的環境下。
感興趣的可以查看我的另一篇文章:基礎篇-工程管理之多Targets
小結
宏的使用,重點是函數宏的使用,后續有新的使用心得會持續更新本文。