DMCApps DMCApps - 5 months ago 36
iOS Question

Parse varargs list of type Class

I am looking to parse out a list of args that are of type Class in iOS Objective-C. I am currently getting a EXEC_BAD_ACCESS whenever I try and parse the list. Here is the implementation to parse the list.

#import <Foundation/Foundation.h>

@interface NSArray (Args)

+ (NSArray *)arg_fromArgs:(id)clazz, ... NS_REQUIRES_NIL_TERMINATION;

@end

#import "NSArray+Args.h"

@implementation NSArray (Args)

+ (NSArray *)arg_fromArgs:(id)clazz1, ... {
NSMutableArray *arguments = [NSMutableArray new];

id eachObject;
va_list argumentList;
if (clazz1)
{
[arguments addObject:clazz1];
va_start(argumentList, clazz1);
while ((eachObject = va_arg(argumentList, id)))
{
[arguments addObject: eachObject];
}
va_end(argumentList);
}

return [NSArray arrayWithArray:arguments];
}

@end


The
while ((eachObject = va_arg(argumentList, id)))
line is where I get the bad access. Here is how I call it:

NSArray *classes = [NSArray arg_fromArgs:[Class1 class], [Class2 class], [Class3 class], nil];


I have tried changing the id variable to Class but that still doesn't do anything. Any ideas on how I can achieve this (aside from passing an array of classes directly in my methods)?

NOTE: This crash only happens on physical devices. It works perfectly fine on the simulators.

Thanks in advance

EDIT

This is how I solved it based on newacct's suggestion. Is there a way to not have to pass the first Class?

+ (NSArray *)arg_fromClass:(Class)clazz andArgsList:(va_list)argumentList {
NSMutableArray *arguments = [NSMutableArray new];
[arguments addObject:clazz];

id eachObject;
while ((eachObject = va_arg(argumentList, id)))
{
[arguments addObject: eachObject];
}
va_end(argumentList);

return [NSArray arrayWithArray:arguments];
}


How I'm calling it.

- (void)someMethod:(Class)clazz, ... {
va_list argumentList;
va_start(argumentList, clazz);
NSArray *classesToSync = [NSArray arg_fromClass:clazz andArgsList:argumentList];

NSLog(@"Syncing Classes: %@", classesToSync);
}


Thanks

Answer

I have looked at the sample app you have posted. There is nothing wrong with your +[NSArray arg_fromClassArgs:] method. It is a varargs method that expects a variable number of object-pointer arguments with a nil argument at the end.

The problem is that you call this method like this in your +[ArgReceiver convertArgsToArray:] method:

[NSArray arg_fromClassArgs:args]

You pass one argument, with no nil argument at the end.

And the way your +[ArgReceiver convertArgsToArray:] method is written (it itself is a varargs method, which takes a variable number of arguments, the first of which you name args) seems to indicate that you intended to "pass off" the variable arguments from that method to another varargs method. But that is impossible -- in C there is no standard way to have a varargs function pass off its arguments to another varargs function.

The only way would be to have a separate version of the +[NSArray arg_fromClassArgs:] method which takes a single va_list argument (maybe call it +[NSArray arg_fromClassArgsVaList:] or something), for other varargs method to call (and which +[NSArray arg_fromClassArgs:] itself can call, so you don't have duplicate code). You can see this in the C API, for example, printf has vprintf, etc., and in Cocoa, -[NSString initWithFormat:] has -[NSString initWithFormat:arguments:]

Comments