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