Cómo crear un object NSInvocation utilizando un método que toma un puntero a un object como argumento

Me gustaría crear un object NSInvocation utilizando un método que toma un puntero a un object NSError como argumento. Un ejemplo de esto sería el método:

- (BOOL)writeToFile:(NSString *)path options:(NSDataWritingOptions)mask error:(NSError **)errorPtr 

Entiendo que establecería mi invocación de esta manera

 NSData *myData = [[NSData alloc] init]; SEL writeToFileSelector = @selector(writeToFile:options:error:); NSMethodSignature *signature = [NSData instanceMethodSignatureForSelector:writeToFileSelector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget:myData]; [invocation setSelector:writeToFileSelector]; NSString *string = [NSString stringWithFormat:@"long cat"]; NSDataWritingOptions *dataOptions; *dataOptions = NSDataWritingFileProtectionComplete; [invocation setArgument:&string atIndex:2]; [invocation setArgument:&dataOptions atIndex:3]; 

Para writeToFile: Opciones: Error: el último argumento espera recibir un puntero en lugar de un object. Como resultado, hacer lo siguiente no funciona:

 NSError *err = nil; [invocation setArgument:&err atIndex:4]; 

Parece lógico que la solución sea crear un puntero a un puntero, pero esto provoca una advertencia del comstackdor. No estoy seguro de cómo ejecutar esto correctamente y no crear un problema de administración de memory.

Crea el argumento exactamente igual que cualquier otro argumento que pasaría al método.

Como señala, la firma del método quiere un NSError ** para su último argumento (índice 4). Entonces, tendrás que declarar uno, pero hay un poco de gotcha.

NSError **errorPointer

Le da una variable que apunta a una variable NSError. Pero, dado que no le ha dicho que apunte a ninguna variable, apunta a cero. Por lo tanto, cuando disparas la invocación, el selector no podrá cambiar la variable a la que apunta tu apuntador de error. En otras palabras, sería como llamar a [myData writeToFile:string options:dataOptions error:NULL] .

Por lo tanto, también querrá declarar una variable NSError y asignar su dirección como la variable a la que su errorPointer debería apuntar:

 NSError *error; NSError **errorPointer = &error; 

Ahora puede pasar el errorPointer como un argumento, y podrá inspeccionarlo más tarde si hubo un problema cuando invocó el método. Echa un vistazo a esta publicación en NSInvocation para get un poco más de ayuda (consejo de Mark Dalrymple por señalar la publicación del blog)

Es importante también darse count de que el scope debe considerarse para los arguments que crea y pasa a su invocación. Echa un vistazo a una pregunta similar que pregunté aquí .

La respuesta aceptada por edelaney05 fue genial, pero creo que necesita un pequeño retoque para ARC. (No puedo agregar comentarios aún, por lo que crear una nueva respuesta para documentar lo que encontré)

Como es, obtuve el error de compilation: "Pointer to non-const type 'NSError *' sin propiedad explícita"

Investigué esto y descubrí que necesitaba:

  NSError * __autoreleasing error = nil; NSError * __autoreleasing *errorPointer = &error; 

Referencias que me llevaron a esta respuesta:

NSInvocation & NSError – __autoreleasing & crasher de memory

Recuento de references automático: puntero a tipo no const 'NSError *' sin propiedad explícita

http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

"__autoreleasing se usa para denotar arguments que se pasan por reference (id *) y se autorean a la devolución".

El tipo de parámetro es NSError ** , que se obtiene al tomar la dirección de un NSError * que desea que se escriba el error. Para establecer un argumento en una NSInvocation , debe pasar la dirección del valor del argumento a setArgument: así que debe poner su NSError ** en una variable (lo llamo errPointer aquí) y tomar la dirección de ese (que será un NSError *** ) para pasar a setArgument: No necesita la variable errPointer adelante.

 NSError *err = nil; NSError **errPointer = &err; [invocation setArgument:&errPointer atIndex:4];