简介
一个可变参数的函数是指该函数拥有不定的参数,参数个数可能为0个,1个或者多个。
在OC中常见的系统可变参数函数有下面两个
- (instancetype)initWithTitle:(nullable NSString *)title
message:(nullable NSString *)message
delegate:(nullable id /**/)delegate
cancelButtonTitle:(nullable NSString *)cancelButtonTitle
otherButtonTitles:(nullable NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION
NS_EXTENSION_UNAVAILABLE_IOS("Use UIAlertController instead.");
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;
NS_REQUIRES_NIL_TERMINATION
是一个宏定义,用于编译时非nil结尾的检查
使用可变参数需要注意的点
- 一个方法最多只能有一个可变参数
- 可变参数一定要放在方法的末尾
- 在可变参数里,多个参数均以逗号隔开
- 一般来说调用可变参数一般会以nil结尾
对于最后一点,在调用NSLog函数的时候,经常会这么写NSLog(@"set : %@", set);
然而并没有在最后添加nil
表示可变参数的结束,这是因为根据前面的format表达式
,可以得知预期的参数个数
,所以不用以nil
来表示结束。
例如
//预期一个参数,所以@"2"这个参数不会被用到,输出test : 1
NSLog(@"test : %@", @"1", @"2");
NSLog(@"test : %@");//运行报错EXC_BAD_ACCESS
NSLog(@"test : %f");//test : 0.000000
NSLog(@"test : %d");//test : 8
创建自己的可变参数方法
在方法末尾添加NS_REQUIRES_NIL_TERMINATION
有一个好处就是在调用该方法的时候,如果可变参数没有以nil
结尾,则系统会出现一个警告Missing sentinel in method dispatch
;如果没有添加这个宏,就不会有这个警告,所以推荐在方法末尾添加这个宏
- (NSString *)books:(NSString *)book, ... NS_REQUIRES_NIL_TERMINATION
{
NSMutableString *str = [NSMutableString new];
if (book) {
[str appendString:book];
NSString *value;
//该指针变量指向可变参数列表
va_list arg_list;
//让arg_list指向第一个可变参数列表的第一个参数,开始提取可变参数列表的参数
va_start(arg_list, book);
//va_arg用于提取arg_list指针当前指向的参数,并将指针移动到下一个参数
//如果当前获取到的参数不为nil,则进行相关操作
while ((value = va_arg(arg_list, NSString *))) {
[str appendFormat:@" + %@", value];
}
//释放arg_list指针,提取结束
va_end(arg_list);
}
return [str copy];
}
NSLog(@"str = %@", [self books:nil]);//str =
NSLog(@"str = %@", [self books:@"Objc", nil]);//str = Objc
NSLog(@"str = %@", [self books:@"Objc", @"Java", nil]);//str = Objc + Java
- (NSInteger)sum:(NSInteger)num, ... NS_REQUIRES_NIL_TERMINATION
{
NSInteger result = num;
NSInteger value = 0;
va_list arg_list;
//如果不添加这句,在调用[self sum:nil]后打印的是sum = 140393006011167,不符合预期
if (arg_list->gp_offset == arg_list->fp_offset) {
return 0;
}
va_start(arg_list, num);
while ((value = va_arg(arg_list, NSInteger))) {
result += value;
}
va_end(arg_list);
return result;
}
NSLog(@"sum = %ld", [self sum:nil]);//sum = 0
NSLog(@"sum = %ld", [self sum:1, 2, nil]);//sum = 3
NSLog(@"sum = %ld", [self sum:1, 3, 2, 4, nil]);//sum = 10
如果在方法中指定可变参数的个数,那么可变参数可以不以nil结尾,不过可能会出现指定个数和参数个数不一致的问题,所以不推荐这个写法
- (NSString *)count:(NSInteger)count books:(NSString *)book, ...
{
NSMutableString *str = [NSMutableString new];
if (book) {
[str appendString:book];
NSString *value;
va_list arg_list;
va_start(arg_list, book);
for (NSInteger i = 1; i < count; i++) {
value = va_arg(arg_list, NSString *);
[str appendFormat:@" + %@", value];
}
va_end(arg_list);
}
return [str copy];
}
NSLog(@"total = %@", [self count:0 books:nil]);//total =
NSLog(@"total = %@", [self count:1 books:@"Objc"]);//total = Objc
NSLog(@"total = %@", [self count:2 books:@"Objc", @"Java"]);//total = Objc + Java
- (NSInteger)sumWithCount:(NSInteger)count values:(NSInteger)num, ...
{
NSInteger result = num;
NSInteger value;
va_list arg_list;
va_start(arg_list, num);
for (NSInteger i = 1; i < count; i++) {
value = va_arg(arg_list, NSInteger);
result += value;
}
va_end(arg_list);
return result;
}
NSLog(@"sum1 = %ld", [self sumWithCount:0 values:nil]);//sum1 = 0
NSLog(@"sum1 = %ld", [self sumWithCount:2 values:1, 2]);//sum1 = 3
NSLog(@"sum1 = %ld", [self sumWithCount:4 values:1, 3, 2, 4]);//sum1 = 10