可变参数

简介

一个可变参数的函数是指该函数拥有不定的参数,参数个数可能为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

你可能感兴趣的:(可变参数)