Search code examples
objective-ccocoacocoa-touchnsarrayvariadic-functions

Creating an NSArray to represent values that include nil


I have data source method which asks for an NSArray of numbers; some of them could be nil, but I need to return every value anyways.

The method might look like this:

- (NSArray *)configurationValues:(Configuration *)configuration {
  ....  
  return [NSArray arrayWithObjects:value1, value2, value3, value4, nil];
}

If the value3 is nil, I have to return {2, 3, (object is nil), 3}.

Off course I'm getting exceptions because I can't insert a nil object.

What I can do is check every object and insert some dummy number or NSNull, but this to me seems like boilerplate, and not really elegant.

I'm trying to create a category for NSArray which would replace all nil values with something else to avoid the exceptions:

@implementation NSArray (Nullable)

+ (instancetype)arrayWithObjectsSafe:(id)firstObj, ... {
    NSMutableArray *array = [NSMutableArray array];
    va_list args;
    id  object;

    va_start(args, firstObj);

    while((object = va_arg(args, id))) {
        if (object) {
            [array addObject:object];
        } else {
            // Insert dummy element // stuck here
            [array addObject:[NSNull null]];
        }
        NSLog(@"OBJECT %@", object);

    }

    va_end(args);
    return [NSArray arrayWithArray:array];
}
@end

I'm not really familiar with variadic methods, and I'm stuck here because the while loop skips the nil value, so the final array contains only the elements before the nil.

Can I fix this, or is there an existing way for NSArray to not throw an exception when trying to insert nil object?


Solution

  • Your code fails simply because you are using nil as both your end of arguments marker and as one of the arguments. Just use a different end of arguments marker. For example you can define:

    const id END_MARKER (__bridge id)(void *)-1;
    

    to define another constant rather like nil (which has the value 0). Change your loop to:

    while((object = va_arg(args, id)) != END_MARKER)
    

    and your call to:

    [NSArray arrayWithObjects:value1, value2, value3, value4, END_MARKER];
    

    and your code works.

    HTH