NSArray to va_list arguments and string format XCode 8

So far I've been using this code snippet:


+ (id)stringWithFormat:(NSString *)format array:(NSArray*)arguments { 
    NSRange range = NSMakeRange(0, [arguments count]); 
    NSMutableData* data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]]; 
    [arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range]; 
    NSString* result = [[NSString alloc] initWithFormat:format arguments:data.mutableBytes]; 
return result;
}


Since update to Xcode 8 I'am getting EXC_BAD_ACCESS on line 5. Any ideas how to create va_list from array using XCode-8?

Replies

What are you expecting this code to do? Can you give me an example of its use.

In general, the set of things that you can legally do with a

va_list
is tiny, so I’m not super surprised that this code was broken by a compiler update. However, before I can make suggestions as to how to proceed I’d like to get a better understanding of your final goạl.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

My expectations are pretty simple. We've got api-endpoints format
e.g settings/user/%@/newSettings/%i
as well as serialized JSONResponse with parameters (string). Paramters are stored in NSArray because of JSONModel mapping - this can be strings, integers etc.. My code requires

initWithFormat: arguments:


being called with data provided from NSArray. That's why I was trying to "pack" data into va_list.
Interesting thing is that I just tested it on xcode7 (with ARM64 architecture checked) and everything seems to be fine.

We've got api-endpoints format e.g

settings/user/%@/newSettings/%i
as well as serialized JSONResponse with parameters (string). [Parameters] are stored in NSArray because of JSONModel mapping - this can be strings, integers etc.

There isn’t a supported way to do this.

va_list
is defined by the C standard and that standard defines a very limited set of operations on it (you can see the full list here). None of those operations let you build a
va_list
except by applying
va_start
within a varargs function. Building a
va_list
by hand may work on some specific architectures but you will inevitably run into problems (either on new architectures, as the OS and compiler evolves).

I also want to clarify the example you showed above. Presumably, the end result is a call like this:

str = [SomeClass stringWithFormat:@"settings/user/%@/newSettings/%i"
    array:@[ @"eskimo1", @42 ]
];

How did your old code work in the

%i
case? When you call
-getObjects:range:
, the value you get back is an
(NSNumber *)
not the
int
that’s specified by the
%i
format specifier.

Consider this code:

NSArray * arguments = @[ @"eskimo1", @42 ];

NSRange range = NSMakeRange(0, [arguments count]); 
NSMutableData* data = [NSMutableData dataWithLength:sizeof(id) * [arguments count]]; 
[arguments getObjects:(__unsafe_unretained id *)data.mutableBytes range:range];
NSLog(@"%@", data);

which prints on my Mac:

<48520000 01000000 272a0000 00000000>

Remember that this is 64-bit big endian, so the actual values are:

  • 0x0000000100005248

  • 0x000000000000272a

The first one is an object pointer for

@"eskimo1"
, which makes sense and is compatible with the
%@
. The second is also an object pointer, but in this case it’s a tagged pointer for @42, and that won’t print properly if you feed it to
%i
(even if you ignore the fact that
%i
expects an
int
, which is 32-bits on our platforms).

If I were in your shoes I’d constrain the allowed format specifiers in the the format string and then manually parse the string, find the format specifiers, and then render that item of the array in a way that’s appropriate for that specifier. This would yield a bunch of advantages:

  • it will actually work (-:

  • it will allow to to check that the array contains the right number of arguments

  • it will allow you to type check each argument (at runtime, but that’s better than nothing)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"