你帶著一個故事去聽一首歌感覺會不一樣,你帶著一個故事去找一首歌心情會不一樣。
宏定義的簡介
宏定義是C提供的三種預處理功能的其中一種,這三種預處理包括:宏定義、文件包含、條件編譯.
1. 不帶參數的宏定義:
格式: #define 標識符 字符串
說明:
(1)宏名一般用大寫
(2)使用宏可提高程序的通用性和易讀性,減少不一致性,減少輸入錯誤和便于修改。例如:數組大小常用宏定義
(3)預處理是在編譯之前的處理,而編譯工作的任務之一就是語法檢查,預處理不做語法檢查。
(4)宏定義末尾不加分號;
(5)宏定義寫在函數的花括號外邊,作用域為其后的程序,通常在文件的最開頭。
(6)可以用#undef命令終止宏定義的作用域
(7)宏定義可以嵌套
(8)字符串" "中永遠不包含宏
(9)宏定義不分配內存,變量定義分配內存。
2. 帶參數的宏定義:
除了一般的字符串替換,還要做參數代換
格式: #define 宏名(參數表) 字符串
例如:#define S(a,b) a*b
(1)實參如果是表達式容易出問題
(2)宏名和參數的括號間不能有空格
(3)宏替換只作替換,不做計算,不做表達式求解
(4)函數調用在編譯后程序運行時進行,并且分配內存。宏替換在編譯前進行,不分配內存
(5)宏的啞實結合不存在類型,也沒有類型轉換。
(6)函數只有一個返回值,利用宏則可以設法得到多個值
(7)宏展開使源程序變長,函數調用不會
(8)宏展開不占運行時間,只占編譯時間,函數調用占運行時間(分配內存、保留現場、值傳遞、返回值)
iOS中常用的宏定義
上面說到宏定義主要分為參數宏和非參數宏,接下來,我們就看一下,在iOS開發過程中我們常用的一些宏定義.
-
尺寸相關
//主屏幕的寬高
#define KmainHight [UIScreen mainScreen].bounds.size.height
#define KmainWidth [UIScreen mainScreen].bounds.size.width
//導航欄高度
#define KnavigationBarHeight (44)
//標簽欄高度
#define KtabBarHeight (49)
-
顏色相關
//普通顏色
#define KmyColor(R,G,B) [UIColor colorWithRed:R/255.0 green:G/255.0 blue:B/255.0 alpha:1.0]
// 隨機顏色
#define KrandomColor [UIColor colorWithRed:arc4random_uniform(256) / 255.0 green:arc4random_uniform(256) / 255.0 blue:arc4random_uniform(256) / 255.0 alpha:1]
-
語言相關
//獲取當前語言
#define KcurrentLanguage ([[NSLocale preferredLanguages] objectAtIndex:0])
-
系統版本相關
//主要用于判斷當前iOS版本號,對其棄用或者未出現的方法進行區別對待
#define KcurrentSystemVersion [[UIDevice currentDevice] systemVersion]
-
手機型號相關
//iPhone5
#define IPHONE5 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(640, 1136), [[UIScreen mainScreen] currentMode].size) : NO)
//iPhone6
#define IPHONE6 ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(750, 1334), [[UIScreen mainScreen] currentMode].size) : NO)
//iPhone6 Plus
#define IPHONE6_Plus ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2208), [[UIScreen mainScreen] currentMode].size) : NO)
//Pad
#define KisPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
-
模擬器相關
/判斷是真機還是模擬器
#if TARGET_OS_IPHONE
//真機
#endif
#if TARGET_IPHONE_SIMULATOR
//模擬器
#endif
-
國際化相關(國際化相關知識不懂點這里)
#define LocalString(string) NSLocalizedString(string, nil)
-
引用相關
//弱引用
#define KweakSelf(type) __weak typeof(type) weak##type = type;
//強引用
#define KstrongSelf(type) __strong typeof(type) type = weak##type;
-
沙盒相關
//獲取沙盒主路徑
#define KhomePath NSHomeDirectory()
//獲取沙盒 Temp
#define KtempPath NSTemporaryDirectory()
//獲取沙盒 Document
#define KdocumentPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
//獲取沙盒 Cache
#define KcachePath [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]
-
打印相關
//對象打印
#define KobjectLog(object) NSLog(@"%@",(object));
//整型數字打印
#define KintNumberLog(number) NSLog(@"%d",(number));
//float數字打印
#define KfloatNumberLog(number) NSLog(@"%f",(number));
-
上線相關
// 自定義NSLog,在debug模式下打印,在release模式下取消一切NSLog(上線時候使用推薦:??????????)
#ifdef DEBUG
#define NSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
#else
#define NSLog(FORMAT, ...) nil
#endif
內聯函數的簡介及其使用
當第一次接觸內聯函數的時候,我認為內斂函數就是宏定義,后來經過度娘的循循教導,我才知道對于內聯函數原來是另有乾坤呀.
為什么要使用內聯函數呢?其實主要是宏定義雖然簡單易懂,但是容易出錯,下面我們就舉例子說明.
比如下面的這個宏定義,其實很簡單,就是比較兩個數的大小.
#define Kmax(a, b) (a) > (b) ? (a) : (b)
然后,我們這樣調用
int result = Kmax(i, j) + 2 ;
其實結果就成了
result = (i) > (j) ? (i) : (j) + 2 ;
而我們需要的是
result = ( (i) > (j) ? (i) : (j) ) + 2 ;
這樣就離我們需要的期望隔著失之毫厘謬以千里....好了,這時候有人就說了,如下改動不就沒有任何問題了?但是真的沒有問題了嗎?
#define Kmax(a, b) ((a) > (b) ? (a) : (b))
如上的修改的確可以修改上面的問題,但是我們如下使用,還是存在問題的
result = Kmax(i++, j);
那么預處理會處理成如下,跟我們的預想的結果還是不一致...
result = (i++) > (j) ? (i++) : (j);
看到上面的問題這么多,相信你們的頭和我一樣是大大的,那么這時候就我們的豬腳--->內聯函數來解決問題了,我們先看一下內聯函數的結構.
NS_INLINE (函數類型,如void,int,id) 函數名 ( 參數a,參數b,..... ) {
..........(函數實現)..........
}
就拿上面的比較方法,我們可以直接寫在PCH文件中.代碼如下.(注意:NS_INLINE是內聯函數的標志,同時要使用內聯函數,需要導入Foundation框架)這樣就完美的解決了上面的問題.
#import <Foundation/Foundation.h>
NS_INLINE int max( int a ,int b){
return a>b?a:b;
}
下面接著是分享給大家的一個內聯函數的應用,一個自定義內聯函數彈窗.(注意:因為UIAlertView被棄用的問題,所以會爆一個黃,但是真的很好用,所以就分享一下)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
NS_INLINE void tipWithMessage(NSString *message){
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];
[alerView show];
[alerView performSelector:@selector(dismissWithClickedButtonIndex:animated:) withObject:@[@0, @1] afterDelay:0.9];
});
}
下面對于調用也是非常的簡單,如下在-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 方法中,調用彈窗函數.
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
tipWithMessage(@"世界,你好!");
}
下面是效果圖.
內聯函數的注意事項
內聯是以代碼膨脹(復制)為代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率。如果執行函數體內代碼的時間,相比于函數調用的開銷較大,那么效率的收獲會很少。另一方面,每一處內聯函數的調用都要復制代碼,將使程序的總代碼量增大,消耗更多的內存空間。以下情況不宜使用內聯:
-
(1)如果函數體內的代碼比較長,使用內聯將導致內存消耗代價較高。
-
(2)如果函數體內出現循環,那么執行函數體內代碼的時間要比函數調用的開銷大。