使用没有计数的va_list方法

时间:2016-03-11 20:59:18

标签: objective-c methods variadic variadic-functions

我在NSArray上编写了一个类别,以便将JavaScript数组方法添加到NSArray。在JavaScript中,splice()方法向数组添加/删除项目。但是添加到阵列的对象数量可能会有所不同。所以我使用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

调试器窗口

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

1 个答案:

答案 0 :(得分:1)

删除count参数没有问题,因为实际上你的&#34;添加&#34;循环似乎不正确。在您使用va_listeachObject = va_arg(argumentList, id);获得第二项后,您永远不会得到另一个对象。您的示例有效的唯一原因是所有后续项目都相同:@"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的解包,并且在重复迭代时可以保留自己的计数器,但问题是必须有哨兵值:一个无法显示为有效列表值的值,表示您已经到了最后。对于对象类型可变参数,通常使用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);

*您添加到您不拥有的课程的方法应始终加上前缀。这有点令人讨厌,但如果您碰巧选择与其他方法相同的名称,那么防止发生灾难性冲突是一种很好的做法。