iOS 小册编程语言爱好者iOS Developer

获取可变参数的方法

2016-11-11  本文已影响93人  fuyoufang

在看 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_startva_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 * 类型。

参考

va_start和va_end使用详解
OC中的可变参数

上一篇 下一篇

猜你喜欢

热点阅读