2011-11-21 11 views
16

Necesito crear en una aplicación iOS una falsificación va_list para pasar a una función NSString initWithFormat:arguments:, este es mi código:va_list falsa en ARC

NSArray *fixedArguments = [[NSArray alloc] initWithArray:arguments]; 

NSRange range = NSMakeRange(0, [fixedArguments count]); 

va_list fakeArgList = (va_list)malloc(sizeof(NSString *) * [fixedArguments count]); 

__unsafe_unretained id *ptr = (__unsafe_unretained id *)fakeArgList; 

[fixedArguments getObjects:ptr range:range]; 

content = [[NSString alloc] initWithFormat:outputFormat 
              arguments:(va_list)fakeArgList]; 
free(fakeArgList); 

El compilador se queja con este mensaje en la línea de reparto:

error: cast of a non-Objective-C pointer type 'va_list' (aka 'char *') to '__unsafe_unretained id *' is disallowed with ARC 

La función getObjects:range: se define como sigue:

- (void)getObjects:(id __unsafe_unretained [])objects range:(NSRange)range; 

He intentado todo, pero todavía no puedo deshacerme de este error ...

¿Existe alguna solución para crear un falso va_list con ARC habilitado? ¿Qué estoy haciendo mal?

Respuesta

30

EDITAR: Esto ya no funciona. Como se preveía en la respuesta inicial, el ABI parece haber cambiado desde debajo de esta respuesta

Jugué un poco y lo puse a funcionar - Comprobé por si había fugas o memoria abandonada y no vi ninguna.

NSArray *fixedArguments = [[NSArray alloc] initWithObjects: @"foo", @"bar", @"baz", nil]; 

    NSRange range = NSMakeRange(0, [fixedArguments count]); 

    NSMutableData* data = [NSMutableData dataWithLength: sizeof(id) * [fixedArguments count]];  

    [fixedArguments getObjects: (__unsafe_unretained id *)data.mutableBytes range:range]; 

    NSString* content = [[NSString alloc] initWithFormat: @"1: %@ 2: %@ 3: %@" arguments: data.mutableBytes]; 

    NSLog(@"%@", content); 

me gusta (ab) uso NSMutableData como esto para conseguir retener el/la semántica de liberación en una cantidad arbitraria de memoria - No es necesariamente relevante para el tema en cuestión, pero es un truco poco.

Como nota para los lectores futuros: falsificar una lista como esta funciona con el ABI actual para MacOS e iOS, pero en general no es portátil, y no es un buen enfoque.

+0

Muchas gracias ... Estoy construyendo una vista selector que, dada una matriz plist contenidos de los diccionarios, un formato de impresión, digamos ' "(% @ - @%)% @"' , y una lista de teclas llena la vista del selector con la cadena formateada extrayendo los datos del archivo plist. La única forma en que encontré usar una impresión formateada con una lista variable de argumentos fue falsificar una va_list. Sé que está lejos de ser una programación limpia, pero no pude encontrar una solución mejor, cualquier alternativa válida es realmente bienvenida y creo que publicaré otra pregunta sobre mi problema para encontrar una solución más limpia. – Scakko

+0

Si siempre está trabajando con% @ y nunca con otros parámetros de tamaño, puede buscar instancias de% @ en la cadena y reemplazarlas por [descripción del objeto] para cada uno de sus parámetros. Mismo efecto, no va_list falso. Pero eso no funcionará para el formateo numérico o cualquier otra cosa, a menos que quieras hacer un montón de trabajo extra. – ipmcc

+0

Lo intentaré hoy, muchas gracias ... – Scakko

0

¡Es posible si está dispuesto a agregar un poco de rapidez a su proyecto!

El bit importante es la asignación de NSArray a [CVarArgType] que es el equivalente rápido para va_list. Si intentas convertir [AnyObject] a [CVarArgType], provocas bloqueos en el tiempo de ejecución, pero con el map podemos hacer explícitamente la lista necesaria.

El resto del código es el contenedor que he creado para poder llamarlo desde obj-c. Puede hacer un contenedor para cualquier función obj-c a la que desee llamar de esta manera.

@objc class StringFormat: NSObject { 
    class func format(key: String, args: [AnyObject]) -> String { 
     let locArgs: [CVarArgType] = args.map({ (arg: AnyObject) -> CVarArgType in 
      if let iArg = (arg is NSNumber ? arg.intValue : nil) { 
       return iArg 
      } 
      return arg as! CVarArgType 
     }); 
     return String(format: key, arguments: locArgs) 
    } 
}