可変長引数を取るメソッドを作成するには

Objective-Cで可変長引数を処理するには、Cと同じようにva_arg系の関数を使用する。

Objective-Cでは、引数の数を受け取るよりも、可変長引数をnil終端で受け取る方が一般的である。可変長引数がnil終端であることを明示するには、メソッドの定義に、NS_REQUIRES_NIL_TERMINATION を指定する。

【基本形】

-(void)someMethod:(id)elements, ... NS_REQUIRES_NIL_TERMINATION {
    va_list args;
    va_start(args, elements);
    id obj = elements;

    while (obj) {
        // 引数に対する処理
        // ...

        obj = va_arg(args, typeof(id));
    }

    va_end(args);
}


可変長引数をNSArrayに変換する例として、以下に循環コレクションクラスのサンプルコードを掲載しておく。

これは、ウロボロスのようにつながった一連のコレクション要素をぐるぐると回りながら順番に参照するために使用する。パラパラ漫画形式のアニメーションのループ再生や、同じ音をいくつか重ねて再生する必要がある場合のサウンドバッファなどに利用できる。

Xcode 5.1, ARCあり

@interface RingBuffer : NSObject
{
    NSArray* _array;
    NSInteger _index;
}

@property(nonatomic, readonly, getter = getSize) NSUInteger size;

-(id)initWithObjects:(id)elements, ... NS_REQUIRES_NIL_TERMINATION;

-(id)next;
-(void)reset;

@end

@implementation RingBuffer

-(NSUInteger)getSize {
    return [_array count];
}

-(id)init {
    self = [super init];
    
    if (self) {
        _index = 0;
        _array = [NSArray array];
    }
    
    return self;
}

-(id)initWithObjects:(id)elements, ... NS_REQUIRES_NIL_TERMINATION {
    self = [self init];
    
    if (self) {
        va_list args;
        
        NSMutableArray* tmp = [NSMutableArray array];
        
        va_start(args, elements);
        __unsafe_unretained id obj = elements;
        
        while (obj) {
            [tmp addObject:obj];
            obj = va_arg(args, typeof(id));
        }
    
        va_end(args);

        _array = [tmp copy];
    }
    
    return self;
}

-(id)next {
    const NSInteger count = [_array count];
    
    if (0 >= count) {
        return nil;
    }
    
    if (count <= _index || 0 > _index) {
        _index = 0;
    }
    
    return [_array objectAtIndex:_index ++];
}

-(void)reset {
    _index = 0;
}

@end