获取可变参数的方法
在看 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 *
类型。