va_list的定義:
VA_LIST 是在C語(yǔ)言中解決變參問(wèn)題的一組宏曲楚,所在頭文件:#include <stdarg.h>
,用于獲取不確定個(gè)數(shù)的參數(shù)。
oc中的定義如下:
#ifndef _VA_LIST
typedef __builtin_va_list va_list;
#define _VA_LIST
#endif
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
宏:
INTSIZEOF:獲取類(lèi)型占用的空間長(zhǎng)度,最小占用長(zhǎng)度為int的整數(shù)倍:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
VA_START:獲取可變參數(shù)列表的第一個(gè)參數(shù)的地址(ap是類(lèi)型為va_list的指針,v是可變參數(shù)最左邊的參數(shù)):
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
VA_ARG:獲取可變參數(shù)的當(dāng)前參數(shù)缘圈,返回指定類(lèi)型并將指針指向下一參數(shù)(t參數(shù)描述了當(dāng)前參數(shù)的類(lèi)型):
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
VA_END:清空va_list可變參數(shù)列表:
#define va_end(ap) ( ap = (va_list)0 )
va_list的說(shuō)明:
函數(shù)參數(shù)是存儲(chǔ)在棧中的劣光,函數(shù)參數(shù)從右往左依次入棧,參數(shù)的存儲(chǔ)如下路
va_list原理圖.png
注意:
- 可變參數(shù)的類(lèi)型和個(gè)數(shù)完全由程序代碼控制,它并不能智能地識(shí)別不同參數(shù)的個(gè)數(shù)和類(lèi)型糟把;
- va_start和va_end成對(duì)出現(xiàn)
va_list的應(yīng)用:
當(dāng)我們有一個(gè)需求:我們需要一個(gè)方法绢涡,但是入?yún)€(gè)數(shù)不確定,這個(gè)時(shí)候就可以使用va_list
- (void)testMethod:(NSString *)string, ...NS_REQUIRES_NIL_TERMINATION{
va_list args;
if (string){
va_start(args, string);
NSString *otherstring = nil;
while ((otherstring = va_arg(args, NSString*))) {
NSLog(@"string = %@ point=%p",otherstring,otherstring);
}
va_end(args);
}
}
調(diào)用: [self testMethod:@"1",@"2",@"3",@(4),@"5",@"6",nil];
方法說(shuō)明:
-
NS_REQUIRES_NIL_TERMINATION
告知編譯器 需要一個(gè)結(jié)尾的參數(shù),告知編譯器參數(shù)的列表已經(jīng)到最后一個(gè)不要再繼續(xù)執(zhí)行下去了遣疯。如果聲明了在調(diào)用方法時(shí)如果沒(méi)有更多的參數(shù)一定加上nil雄可,否則會(huì)一直循環(huán)取出參數(shù)造成崩潰. -
va_start(args, string)
獲取可變參數(shù)列表的第一個(gè)參數(shù)的地址,在這里是獲取string的內(nèi)存地址,這時(shí)args的指針 指向string -
va_arg(args, NSString*)
獲取可變參數(shù)的當(dāng)前參數(shù),返回指定類(lèi)型并將指針指向下一參數(shù),第一次循環(huán)就會(huì)把指針指向上圖的p2,下一次就會(huì)指向p3.直到結(jié)束缠犀,什么時(shí)候結(jié)束呢数苫?如果我們不判斷篩選條件,可以一直取出數(shù)據(jù)辨液,我們通常直到內(nèi)存空間取到數(shù)據(jù)為nil時(shí)或者0時(shí)結(jié)束虐急。 -
va_arg(args, NSString*)
中會(huì)將指針指向下一參數(shù),那應(yīng)該偏移多少是到下一個(gè)參數(shù)地址呢滔迈?會(huì)等于與va_arg宏所返回的數(shù)值具有相同類(lèi)型的對(duì)象的長(zhǎng)度止吁,比如:都是NSString類(lèi)型,這時(shí)候偏移8字節(jié)燎悍,如果參數(shù)中混有其他類(lèi)型的數(shù)據(jù)敬惦,如int,這時(shí)候偏移8字節(jié)能取到正確的數(shù)據(jù)嗎谈山,肯定是有問(wèn)題的俄删,所以當(dāng)我們?cè)趥鲄?shù)時(shí),多個(gè)參數(shù)的類(lèi)型要盡量一樣
系統(tǒng)的NSLog也是用到va_list勾哩,va_list不會(huì)知道我們參數(shù)的數(shù)據(jù)類(lèi)型和個(gè)數(shù)抗蠢,這就是為什么我們需要占位符的原因
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
所以va_list很傻举哟,很不智能.......