亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

Objective-C 宏定義詳細介紹

2020-07-26 03:08:44
字體:
來源:轉載
供稿:網友

喜歡讀一些開源項目源碼的人,總是會發現,大神的代碼中總是有那么一些簡短而高效的宏定義,點擊進去一看,發現晦澀難懂,別說學習了,有時候理解都是一種困難,但是宏定義本身并沒有那么難,但是寫出一個好的宏當然還是需要豐富的經驗和技術,接下來就說一說宏定義,看懂大神的宏是第一步,偶爾寫一個也是裝逼的好辦法~

定義:

宏定義分為兩種:一種是對象宏(object-like macro)另一種就是函數宏(function-like macro)

根據名字也可以理解到,對象宏就是用來定義一個量,通過這個宏可以拿到這個變量,比如我們定義一個π值: #define PI 3.1415926在這里如果用到π值時,就不需要再寫出一個浮點數了,而直接使用PI就相當寫入了這個常量浮點數,其本質的意義在于把代碼中的PI在編譯階段替換為真正的常量,一般用來定義一些常用的常量,比如屏幕的寬高、系統版本號等。但是需要注意的是,但你定義一個表達式為宏的時候,需要透過宏的表面,看到器編譯的本質,例如

#define MARGIN  10 + 20

但你用它來計算一個寬度時,如果用到了MARGIN * 2,結果將會非你所愿,你得到的會是一個50而并非60,展開表達式就可以看到

MARGIN * 2 // 展開可以得到
//  10 + 20 * 2  = 50

我們需要考慮到它的運算優先級,解決的方式很簡單,再它的外層加上一個小括號

#define MARGIN (10 + 20)// MARGIN * 2// (10 + 20) * 2 = 60

函數宏的作用就類似于一個函數一樣,它可以傳遞參數,通過參數進行一系列的操作,比如我們常用的計算兩個數的最大值,我們可以這樣來定義

#define MAX(A,B) A > B ? A : B

這樣寫看起來是沒有問題的,進行簡單的比較MAX(1,2)發現也是沒有什么問題,但是當有人使用你的宏進行更加復雜的計算時就回出現新的問題,比如進行三個數值的計較時,可能會這樣寫

int a = 3;int b = 2;int c = 1;MAX(a, b > c ? b : c) //= 2

結果肯定也不是你想要的,最大值很明顯是3,但是計算的結果確實2,這其中發生了什么導致計算出錯,我們可以展開宏來一探究竟,下面是宏的展開

MAX(a,b > c ? b : c);//a > b > c ? b : c ? a : b > c ? b : c//(a > (b > c ? b : c) ? a : b) > c ? b : c // 這是運算的優先級// 帶入值可以看出//( 3 > (2 > 1 ? 2 : 1 ) ? 3 : 2) > 1 ? 2 : 1// (3 > 2 ? 3 : 2) > 1 ? 2 : 1// 3 > 1 ? 2 : 1

想必大家都看出來了問題所在,還是由于優先級的問題,所以在此謹記,反正多寫兩個括號也不會累著,不管會不會出現問題, 寫上小括號終究是保險一些~
可是總有寫奇葩的寫法會出現,而且看開起來還很有道理的樣子~

c = MAX(a++,b); // **我直接展開給你看就得了**// c = a++ > b ? a++ : b// c = 3++ > 2 ? 3++ : 2// c = 4// a = 5

不管這樣寫的那個人是有多欠揍,但是畢竟看起來是沒有任何問題的,所有我們要處理這樣的情況,但是使用我們普通的小括號已經無法解決,我們需要使用賦值擴展({...})相信有朋友已經認出來了這種用法了,我們可以使用這樣的方法來計算出一個對象,而不用浪費變量名,可以形成小范圍的作用域來計算特殊的值

int a = ({ int b = 10; int c = 20; b + c;})// a = 30;int b; // 繼續使用b和c當變量名也沒有問題int c;

再回到現在這個問題上,我們該如何改裝這個宏來讓其適應這個坑爹的寫法呢

#define MAX(A,B) ({__typeof(A) __a = (A);__typeof(B) __b = (B); __a > __b ? __a : __b; })

__typeof()就是轉換為相同類型的變量值,就完美的解決了這個問題,但是還有一個不怎么會發生的意外,通過上面也可以知道,我們生成了新的變量__a, __b,如何有人使用了__a,__b,就會應為變量名重復而編譯錯誤,如果有人這樣用了,你可以拿起你的鍵盤砸他一臉,原因當然不是__a使你的宏錯誤了,而是__a到底是什么意思,變量名的重要性不言而喻,除非你和看代碼的人有仇,否則請使用有意義的變量名,接下來讓我們看一看官方的MAX是如何實現的

#define __NSX_PASTE__(A,B) A##B#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

這是Function框架中的MAX定義,我么來一步一步的解析它,首先看見的是

#define __NSX_PASTE__(A,B) A##B
// 將A和B連接到一塊

它的作用是將A和B連接到一塊,用來生成一個的字符串,比如A##12就成了A12

接下來我們看到了一個有三個參數的宏定義__NSMAX_IMPL__(A,B,__COUNTER__)

#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

我們先來解釋__COUNTER__是什么,__COUNTER__是一個預編譯宏,它將會在每次編譯時加1,這樣的話可以保證__NSX_PASTE__(__b,__CONNTER__)生成的變量名不易重復,但是這樣還是有那么點危險,就是你要是起變量名叫__a20,那就真的真的沒有辦法了~

可變參數宏

說起可變參數,我們用的最多的一個方法NSLog(...)就是可變參數了,可變參數意味著參數的個數是不定的,而NSLog作為我們調試時一個重要的工具實在時太廢物了,只能打印對應的時間和參數信息,而文件名,行數,方法名等重要的信息都沒有給出,今天我們就借此來實現一個超級版NSLog宏~~~

#define NSLog(format, ...) do { fprintf(stderr, "<%s : %d> %s/n", /[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__); /(NSLog)((format), ##__VA_ARGS__); /fprintf(stderr, "-------/n"); / } while (0)

首先看這個宏的定義NSLog(format,...)發現它有...,這就是可變參數,而__VA__ARGS__就是除了format外剩下的所有參數,接下來我們發現使用了一個do{}while(0)循環,說明這個循環只執行一便就回停止,感覺廢話啊,我們的目的就是只執行一遍啊,但這樣寫又是為了進行防御式編程,如果有人這樣寫的話

if (100 > 99)
  NSLog(@"
%@",@"Fuck");

就會出現無論如何都會執行后兩個打印,出現的問題想必大家也都知道,那我們直接使用{}給擴起來不就行了,實際操作后確實是解決了這個問題,但是再擴展一下,當我們使用了if{} else if{}時又會出現新的問題

if (100 > 99) NSLog(@"%@",@"Fuck");else {}// 展開后可得if (100 > 99){ fprintf(stderr, "<%s : %d> %s/n", [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__); (NSLog)((format), ##__VA_ARGS__); fprintf(stderr, "-------/n");};else {}

編譯錯誤,大家也發現了NSLog后面會跟上;,如果我么直接使用了{}后,會在編譯時在外面加上;,導致編譯錯誤,而使用了do{} while(0)循環后就不會出現這個問題了

if (100 > 99) do { fprintf(stderr, "<%s : %d> %s/n", [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, __func__); (NSLog)((format), ##__VA_ARGS__); fprintf(stderr, "-------/n");} while(0);else {}

到此位置問題解決的差不多了,看一下內部的結構,__FILE__是編譯的文件路徑,__LINE__是行數,__func__是編譯的方法名,下面我們又看見了

(NSLog)((format), ##__VA_ARGS__);

##上面已經看見過了,在這里的作用差不多,也是連接的意思,__VA_ARGS__是剩下的所有參數,使用##連接起來后就時NSLog(format,__VA_ARGS__)了,這就是NSLog的方法了,但是不知道有沒有人發現一個細節,如果__VA_ARGS__為空的話,那豈不是成了NSLog(format,)這樣肯定會編譯報錯的,但是蘋果的大神們早就想到了解決的方法,如果__VA_ARGS__為空的話,在這里##將會吞掉前面的,,這樣一來就不會出問題了。然后我們就可以使用這個強大的NSLog()了。

接下說一下多參數函數的使用

- (void)say:(NSString *)code,... {    va_list args;  va_start(args, code);  NSLog(@"%@",code);  while (YES) {    NSString *string = va_arg(args, NSString *);    if (!string) {      break;    }    NSLog(@"%@",string);  }  va_end(args);}

我們可以要先定義一個va_list args來定義多參數變量args,然后通過va_start(args, code)來開始取值,code是第一個值,va_arg(args, NSString *)來定義取出的值類型,取值方式有點像生成器,取完之后調用va_end(args)來關閉。這就是整個過程,平時很少使用這樣的方法,如果你有什么好的實用方法請評論指教~~~

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美巨乳在线观看| 丝袜美腿亚洲一区二区| 北条麻妃一区二区三区中文字幕| 色综合久久悠悠| 茄子视频成人在线| 一个色综合导航| 一区二区在线免费视频| 日韩欧美在线免费观看| 久久精品最新地址| 97久久超碰福利国产精品…| 美女精品久久久| 57pao成人国产永久免费| 久久精品视频网站| 欧美小视频在线观看| 97国产suv精品一区二区62| 久久久久五月天| 亚洲欧美日韩高清| 黑人狂躁日本妞一区二区三区| 亚洲精品www久久久| 日韩在线观看视频免费| 最近2019年好看中文字幕视频| 亚洲一区二区久久久久久| 欧美xxxx18性欧美| 91国产在线精品| 国产欧美精品一区二区三区-老狼| 日韩免费观看在线观看| 国产精品美女呻吟| 久久久国产一区| 日韩h在线观看| 久久久久久久久久久免费| 91大神在线播放精品| 97久久精品人人澡人人爽缅北| 国产精品黄色影片导航在线观看| 欧美国产日产韩国视频| 久久人人爽人人爽人人片av高清| 伊人青青综合网站| 在线观看视频亚洲| 黑人精品xxx一区| 国产精品第七十二页| 久久久久这里只有精品| 日韩精品有码在线观看| 亚洲最大激情中文字幕| 国产精品观看在线亚洲人成网| 亚洲精品欧美日韩专区| 亚洲精品动漫100p| 啊v视频在线一区二区三区| 97视频在线观看播放| 国产裸体写真av一区二区| 欧美精品亚州精品| 欧美极品欧美精品欧美视频| 国产ts一区二区| 九九热视频这里只有精品| 午夜精品一区二区三区在线视频| 国产亚洲精品一区二区| 97免费在线视频| 最好看的2019的中文字幕视频| 97热精品视频官网| 国产精品大片wwwwww| 7m第一福利500精品视频| 疯狂做受xxxx欧美肥白少妇| 69久久夜色精品国产69乱青草| 伊人久久综合97精品| 国产69精品久久久| 538国产精品一区二区免费视频| 欧美裸体xxxx极品少妇| 日韩在线观看免费全集电视剧网站| 色视频www在线播放国产成人| 欧美激情一区二区三级高清视频| 尤物yw午夜国产精品视频| www.欧美视频| 亚洲精品一区二区三区婷婷月| 欧洲成人免费视频| 日韩欧亚中文在线| 欧美中文字幕在线播放| 欧美精品成人91久久久久久久| 成人有码在线视频| 欧美噜噜久久久xxx| 欧美成人精品激情在线观看| 国产精品va在线| 在线观看日韩欧美| 日韩视频精品在线| 欧美中文字幕在线视频| 国产在线一区二区三区| 亚洲成人久久久久| 国产精品久久久久久中文字| 久久中国妇女中文字幕| 欧美视频在线免费| 91久久综合亚洲鲁鲁五月天| 欧美精品18videos性欧| 日韩电影免费观看在线| 国产一区二区视频在线观看| 欧美激情综合色综合啪啪五月| 深夜福利亚洲导航| 91色在线观看| 精品久久久一区| 69av成年福利视频| 国产精品女主播| 欧美一区视频在线| 中文字幕欧美精品日韩中文字幕| 91在线视频免费| 中国china体内裑精亚洲片| 国产精品一区二区三区成人| 精品视频中文字幕| 欧美日韩中文字幕综合视频| 欧美高清视频在线| 欧美午夜美女看片| 91高清视频在线免费观看| 久久精品久久久久久| 国产精品99久久久久久www| 国产精品国产自产拍高清av水多| 精品久久久久久国产| 亚洲欧美中文日韩v在线观看| 精品亚洲国产视频| 国产脚交av在线一区二区| 欧美成人精品三级在线观看| 尤物yw午夜国产精品视频明星| 中文字幕精品一区二区精品| 川上优av一区二区线观看| 成人免费看黄网站| 国产中文字幕亚洲| 欧美日韩国产一区二区三区| 中文字幕在线看视频国产欧美| 久久久久久久久国产精品| 国内免费精品永久在线视频| 欧美不卡视频一区发布| 81精品国产乱码久久久久久| 亚洲成人激情小说| 久久国产加勒比精品无码| 精品国产老师黑色丝袜高跟鞋| 亚洲欧美一区二区三区久久| 成人h视频在线观看播放| 欧美精品videosex性欧美| 国产精品自拍小视频| 亚洲免费伊人电影在线观看av| 国产精品久久久久久久7电影| 国产精品自拍小视频| 日韩在线视频线视频免费网站| 成人免费网站在线看| 欧美一级电影久久| 国产精品自拍偷拍视频| 国产亚洲一级高清| 国内成人精品一区| 久久久免费av| 91在线免费视频| 91免费国产视频| 亚洲国产一区二区三区在线观看| 秋霞av国产精品一区| 这里只有视频精品| 国产91免费观看| 国产一区二区av| 久久成人精品一区二区三区| 日韩精品中文字幕久久臀| 成人精品网站在线观看| 亚洲欧美制服第一页| 日本免费久久高清视频| 久久久视频免费观看| 影音先锋欧美精品| 国产精品一区久久| 97精品国产aⅴ7777| 精品亚洲一区二区| 久久精品国产一区二区电影| 一本色道久久88综合日韩精品| 成人av色在线观看| 色视频www在线播放国产成人|