你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> Objective-C 宏界說具體引見

Objective-C 宏界說具體引見

編輯:IOS開發綜合

愛好讀一些開源項目源碼的人,老是會發明,年夜神的代碼中老是有那末一些冗長而高效的宏界說,點擊出來一看,發明艱澀難明,別說進修了,有時刻懂得都是一種艱苦,然則宏界說自己並沒有那末難,然則寫出一個好的宏固然照樣須要豐碩的經歷和技巧,接上去就說一說宏界說,看懂年夜神的宏是第一步,偶然寫一個也是裝逼的好方法~

界說:

宏界說分為兩種:一種是對象宏(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)來封閉。這就是全部進程,日常平凡很少應用如許的辦法,假如你有甚麼好的適用辦法請評論指教~~~

感激浏覽,願望能贊助到年夜家,感謝年夜家對本站的支撐!

【Objective-C 宏界說具體引見】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved