在看 YTKNetwork 源碼的時候,看到下面的 YTKLog 定義(刪除了部分代碼):
void YTKLog(NSString *format, ...) {
#ifdef DEBUG
va_list argptr;
va_start(argptr, format);
NSLogv(format, argptr);
va_end(argptr);
#endif
}
接下來對下面的代碼進行解析。
可變參數(shù)
當我們無法列傳遞給函數(shù)的所有的實參類型和數(shù)目時,可以用省略號指定參數(shù)列表。例如 NSLog 的定義:
void NSLog(NSString *format, ...);
函數(shù)參數(shù)傳遞的原理
函數(shù)參數(shù)時,參數(shù)存放在內存的堆棧段中,在函數(shù)執(zhí)行的時候,以數(shù)據(jù)結構棧的形式進行傳遞的,從右至左依次入棧。舉個例子:
void func(int x, float y, char z);
在函數(shù)調用的時候,實參 char z 先進棧,然后時 float y ,最后時 int x ,因此在內存中的變量存放依次是 x -> y -> z ,因此,從理論上說,我們只要探測到任意一個變量的地址,并且知道其他變量的類型,通過指針位移運算,就可以順藤摸瓜的找到其他變量了。
相關宏定義
typedef char* va_list;
va_list 是一個字符指針,可以理解為指向當前參數(shù)的一個指針,取參必須通過這個指針進行。
void va_start ( va_list ap, prev_param ); /* ANSI version */
對 ap 進行初始化,讓它指向可變參數(shù)表里面的第一個參數(shù),第一個參數(shù)是 va_list 類型的指針,第二個參數(shù) prev_param 是在變參表前面緊挨著的變量,即 “…” 之前的那個參數(shù)。
type va_arg(va_list ap, type);
獲取參數(shù),第一個參數(shù)是 ap,第二個參數(shù) type 是獲取指定參數(shù)的類型,然后返回這個指定的類型的值,并且把 ap 指向可變參數(shù)的下一個變量的位置。
void va_end( va_list ap );
獲取所有的參數(shù)之后,我們必須將這個 ap 指針關閉,以避免發(fā)生危險。它將輸入的參數(shù) ap 置為 NULL。通常 va_start 和 va_end 是成對出現(xiàn)的。
以下是 NSLogv 的定義,一個是說明格式化的字符串 format,一個是可變參數(shù)的指針 args。
void NSLogv(NSString *format, va_list args);
實例
以下是一個可變參數(shù)的方法的定義,內部有獲取可變參數(shù)的方法:
- (void)testFunc:(NSString *)parameter1, ... {
NSString *parameter = nil;
va_list ap;
va_start(ap, parameter1);
while (1) {
parameter = va_arg(ap, NSString *);
if (!parameter) {
break;
}
NSLog(@"%@", parameter);
}
}
調用如下:
[self testFunc:@"para", @"one" @"two", @"three", nil];
其他
- 格式化字符串的要求
NSString *obj = @"A string or other object.";
NSLog(obj);// 有警告
注:警告信息:“Format string is not a string literal (potentially insecure)”,說明 NSLog 要求的參數(shù)為字面量,不可為 NSString * 類型。