一、使用場景
? ? ? ?有時候我們需要自定義一個方法來實現(xiàn)一些功能,(比如:暴打小卷),我們的方法里面需要指定誰來暴打小卷,打小卷的人數(shù)不確定,打小卷的是不是人也無法確定,所以我們需要用到可變參數(shù)。
二、簡單介紹
? ? ? ?簡單來說,可變參數(shù)就是我們在自定義方法的時候,傳遞的參數(shù)類型,參數(shù)個數(shù)可以根據(jù)需要來改變。實現(xiàn)像下面這樣去調(diào)用這個方法:第一個參數(shù)固定,其它參數(shù)類型不同,參數(shù)個數(shù)也不一樣。當(dāng)然,我們定義兩個方法也可以實現(xiàn),但是如果李成鵬每次打小卷帶的小弟都不一樣的話呢?
[self 暴打小卷 : 李成鵬,王冬,尉超,貓,狗,nil];
[self 暴打小卷 : 李成鵬,張洪林,王鼎,豬,nil];
三、可變參數(shù)的實現(xiàn)原理
1.在C中,當(dāng)我們無法列出傳遞函數(shù)的所有實參的類型和數(shù)目時,可以用省略號指定參數(shù)表,OC中自然也可以這么玩:
- (void)暴打小卷:(人 *)李成鵬, ...;
2.函數(shù)的參數(shù)在內(nèi)存中是以棧的形式存儲的,存儲順序是按照參數(shù)從右往左的順序依次入棧,根據(jù)棧的先進后出的特性,接下來取參數(shù)的時候就是從左向右的
順序依次取值了。從理論上講,只要取到了其中一個參數(shù)的地址,“順藤摸瓜"總能找到其它的參數(shù)地址。所以,需要將第一個參數(shù)固定。根據(jù)第一個參數(shù)的地址,來取到其它的參數(shù)。
3.在寫具體的實現(xiàn)方法之前,我們需要用到C庫里面的幾個”工具“來”暴打小卷“(動家伙,打扁他^_^)。這些工具有:刀、槍、棍、棒。。。(⊙o⊙)…其實是:
typedef char *?va_list :這是一個字符類型的指針,指針指向當(dāng)前的參數(shù),需要通過這個指針取參數(shù)。
void va_start(va_list ap,param) :初始化字符指針的函數(shù),將指針指向方法中的第一個可變參數(shù),這個函數(shù)需要兩個參數(shù):va_list和自定義的方法中固定的那個參數(shù)(比如暴打小卷中的“李成鵬”)
type va_arg(va_list ap,type):這是一個取參數(shù)的函數(shù),這個函數(shù)需要兩個參數(shù),第一個參數(shù)是字符指針va_list,第二個參數(shù)是我們需要取的可變參數(shù)的數(shù)據(jù)類型。這個函數(shù)需要做兩件事:1:取到指定數(shù)據(jù)類型的參數(shù)。2:將指針ap指向下一個可變參數(shù)的地址。
void va_end(va_list ap):當(dāng)我們?nèi)⊥晁械目勺儏?shù)之后,需要將指針ap指向NULL。不然它就可能嚇JB亂指,就成了一個野孩子:野指針。這個函數(shù)需要和va_start成對使用。
4.通過上面第三條說的,其實實現(xiàn)可變參數(shù)的步驟已經(jīng)顯而易見了:
1、定義一個va_list指針,用來指向打小卷的兇手們。警察開始立案調(diào)查%>_<%。
2、調(diào)用 va_start 初始化這個指針,首先指向打小卷的幕后主使(李成鵬)抓到他之后就能抓到其他兇手。
3、調(diào)用 va_arg 開始取參數(shù),主使抓到了,小弟們自然跑不掉。
4、調(diào)用 va_end 將va_list指針置空??梢越Y(jié)案了O(∩_∩)O~~。
四、具體實現(xiàn)
不多說,直接上代碼實例:
// 定義一個方法
-(void)暴打小卷:(人*)主使,...{
// 定義指針
va_list ap;
// 開始取值,指針先確定主使人
va_start(ap,主使);
NSLog(@"打小卷的主使是:%@",主使);
// 循環(huán)取值
while(YES){
// 因為不確定參數(shù)的類型,所以指定數(shù)據(jù)類型是id類型,并且用id類型的變量來接收。
id obj=va_arg(ap,id);
// 取完所有參數(shù)之后,跳出循環(huán)
if(obj==nil)break;
NSLog(@"打小卷的幫兇有:%@",obj);
}
// 取完之后毀掉va_list指針
va_end(ap);
}
調(diào)用:簡單介紹里面已經(jīng)寫了調(diào)用的示例,需要注意的是,參數(shù)的結(jié)尾必須寫上nil來讓取參數(shù)的時候的循環(huán)停止。當(dāng)然機智的你或許有其他的辦法來控制循環(huán)次數(shù),就不需要寫最后的nil了。比如對sql語句的?進行賦值的時候,可以判斷問號的個數(shù)來控制循環(huán)次數(shù)。