使用没有计数的 va_list 方法

Using a va_list method without a count

我正在 NSArray 上编写一个类别以将 JavaScript 数组方法添加到 NSArray。在 JavaScript 中,splice() 方法都 adds/removes 项 to/from 一个数组。但是添加到数组中的对象数量可能会有所不同。所以我使用 va_list 来允许更灵活地输入对象值。

就目前而言,该方法需要一个计数输入值。没有一个我怎么能重写这个?

界面

@interface NSArray (JavaScriptArray)

- (NSArray *)splice:(NSUInteger)index remove:(NSUInteger)remove count:(NSUInteger)count arguments:(id)firstObject,...;

@end

实施

@implementation NSArray (JavaScriptArray)

- (NSArray *)splice:(NSUInteger)index remove:(NSUInteger)remove count:(NSUInteger)count arguments:(id)firstObject,...
{
    NSMutableArray *mSplice = [NSMutableArray arrayWithArray:self];

    if (remove != 0) {
        NSUInteger removeIndex = index;

        for (NSUInteger i = 0; i < remove; i++) {
            [mSplice removeObjectAtIndex:removeIndex];
            removeIndex = removeIndex + 1;
        }
    }

    if (count != 0) {
        NSUInteger addIndex = index;

        id eachObject;
        va_list argumentList;
        if (firstObject) {
            [mSplice insertObject:firstObject atIndex:addIndex];
            addIndex = addIndex + 1;
            va_start(argumentList, firstObject);
            eachObject = va_arg(argumentList, id);

            for (NSUInteger i = 0; i < count; i++) {
                [mSplice insertObject:eachObject atIndex:addIndex];
                 addIndex = addIndex + 1;
            }

            va_end(argumentList);
        }

    }

    return [NSArray arrayWithArray:mSplice];
}

@end

调用方法

- (void)viewDidLoad 
{
    [super viewDidLoad];

    NSArray *fruit = @[@"Banana", @"Orange", @"Apple", @"Mango"];

    NSArray *fruitSplice = [fruit splice:2 remove:0 count:4 arguments:@"Lemon", @"Kiwi", @"Kiwi", @"Kiwi"];
    NSLog(@"fruitSplice %@", fruitSplice);
}

@end

调试器Window

fruitSplice (
             Banana,
             Orange,
             Lemon,
             Kiwi,
             Kiwi,
             Kiwi,
             Kiwi,
             Apple,
             Mango
             )

删除 count 参数没有问题,因为实际上您的 "add" 循环似乎不正确。在您使用 eachObject = va_arg(argumentList, id);va_list 中获得第二个项目后,您再也不会获得另一个对象。您的示例起作用的唯一原因是所有后面的项目都相同:@"Kiwi"。如果您的测试电话是

NSArray *fruitSplice = [fruit splice:2 
                              remove:0 
                               count:4 
                           arguments:@"Lemon", @"Albatross", @"Kiwi", @"Kiwi"];

你会看到

fruitSplice (
             Banana,
             Orange,
             Lemon,
             Albatross,
             Albatross,
             Albatross,
             Albatross,
             Apple,
             Mango
             )

作为输出。

您需要重做 va_list 的解包,并且您可以在迭代它时保留自己的计数器,但要注意的是必须有一个 sentinel 值:一个不可能作为有效列表值出现的值,表示您已经到了最后。对于对象类型的可变参数,您通常会使用 nil 作为标记。

当您使用 va_list 时,您必须 哨兵 计数。您没有其他方法可以知道何时停止弹出参数。

你的签名可以变成*:

- (NSArray *)KRSpliceAt:(NSUInteger)index 
          removingCount:(NSUInteger)remove 
          addingObjects:(id)firstObject, ... NS_REQUIRES_NIL_TERMINATION;

NS_REQUIRES_NIL_TERMINATION 是严格可选的,但如果在没有标记的情况下调用该方法,编译器会通知您。

然后你的添加循环改变:

// Insertion index starts at given splice point
NSUInteger addIndex = index;
// Initialize the va_list
va_list objs;
va_start(objs, firstObj);
// Start at the beginning
id nextObj = firstObj;
// Test for sentinel nil
while( nextObj ){
    [mSplice insertObject:nextObj atIndex:addIndex];
    // Update insertion point
    addIndex++;
    // Get next argument
    nextObj = va_arg(objs, id);
}
// Signal completion of list
va_end(objs);

*您添加到 类 您不拥有的方法应始终加上前缀。这有点烦人,但如果您碰巧选择了与另一个方法相同的名称,这是防止灾难性冲突的好习惯。