前言
在OC和Swift都是允许使用可变参数方法的,虽然可变参数方法在很大程度上违反了编码规范,但是在一些特定场景却又很好用。例如我们在OC中常用的方法NSLog
以及Swift中使用的print
。
在Swift中定义可变参数方法
class VariableArgumentsFunction: NSObject {
func variableArg1(args: Any...) -> Void {
print(args)
}
func variableArg2(first: Any, _ args: Any...) -> Void {
print(first)
print(args)
}
}
在Swift中定义可变参数方法很简单,参数的类型后面加上...
就可以了。在使用时,调用方传值的可变参数会以数据的形式存放在args
中。
在OC中定义可变参数方法
+ (void)variableArg:(id)arg,... NS_REQUIRES_NIL_TERMINATION {
va_list args;
va_start(args, arg);
NSLog(@"%@", arg);
while (1) {
id arg = va_arg(args, id);
if (arg == nil) {
break;
}
NSLog(@"%@", arg);
}
va_end(args);
}
+ (void)variableArg1:(id)arg1 arg2:(id)arg2, ... NS_REQUIRES_NIL_TERMINATION {
va_list args;
va_start(args, arg2);
NSLog(@"%@", arg1);
NSLog(@"%@", arg2);
while (1) {
id arg = va_arg(args, id);
if (arg == nil) {
break;
}
NSLog(@"%@", arg);
}
va_end(args);
}
在OC中定义可变参数方法会略显复杂。
方法声明时在参数后面加 , ...
,最好是为方法加上NS_REQUIRES_NIL_TERMINATION
进行修饰,这个宏定义是提示调用者这个方法的参数需要以nil
结尾。
方法体中需要使用va_list
来获取可变参数列表,关于va_list
会在后面的内容进行描述。
调用方法时需要注意:
- 可变参数的类型都需要与
, ...
前面的这个参数的类型一致。 - 如果不以
nil
结尾,在va_arg
时会报错。
关于va_list
VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件:#include <stdarg.h>,用于获取不确定个数的参数。
在OC中va_list
的定义如下:
typedef __darwin_va_list va_list;
其它几个相关函数定义如下:
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
本篇文章不对这些函数进行深入的挖掘,只简单的解释这些函数的用法。
-
va_list
用于承载可变参数列表。在方法体中使用va_list args;
来让args
变量接收可变参数列表 -
va_start
获取可变参数列表的第一个参数的地址(ap
为va_list
,param
为方法声明中可变参数左边的那一个参数。也就是上面第一个方法中定义的arg
,第二个方法中的arg2
) -
va_arg
获取可变参数的当前参数,返回指定类型并将指针指向下一参数(type为参数的数据类型) -
va_end
清空va_list可变参数列表
在方法体中有几点需要注意:
-
va_list
中不包含可变参数左边的那一个(上面第一个方法中定义的arg
,第二个方法中的arg2
),所以在使用的时候需要注意。 - 参数的类型需要在方法体中自行控制,编译器不会对参数的类型进行严格检查,所以使用的时候需要考虑做类型校验,防止报错。
- 使用完参数之后,记得调用
va_end
,防止出现灵异情况。