NSInvocación y problemas de memory

Entonces vengo del mundo de Java, donde ignoramos los problemas de gestión de la memory. En su mayor parte, ARC me ha salvado el culo, pero aquí hay algo que me dejó perplejo. Básicamente estoy usando NSInvocations para algunas cosas, y me encontré con algunos desagradables problemas de memory antes de realizar las siguientes modificaciones de código. Desde que hice estas modificaciones, los lockings de memory se han ido, pero generalmente tengo mucho miedo del código que no entiendo. ¿Estoy haciendo esto bien?

Antes: todo tipo de problemas de memory:

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]]; [invocation setSelector:selector]; [invocation setTarget:target]; [invocation setArgument:&data atIndex:2]; [invocation setArgument:&arg atIndex:3]; [invocation invoke]; NSString *returnValue; [invocation getReturnValue:&returnValue]; 

Después: No hay problemas de memory, pero no estoy seguro de haber acertado:

 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[[target class] instanceMethodSignatureForSelector:selector]]; [invocation setSelector:selector]; [invocation setTarget:target]; [invocation setArgument:&data atIndex:2]; [invocation setArgument:&arg atIndex:3]; [invocation invoke]; CFTypeRef result; [invocation getReturnValue:&result]; if (result) CFRetain(result); NSString *returnValue = (__bridge_transfer NSString *)result; 

Editar:

Solo quería agregar sobre la base de la respuesta a continuación, usé objc_msgSend, como tal:

 NSString * returnValue = objc_msgSend(target, selector, data, arg); 

Y soluciona todos los problemas de memory, además de que se ve mucho más simple. Comente si ve algún problema con esto.

Responderé a tu pregunta de esta manera: No uses NSInvocation . Es solo un consejo amigable para evitar eso si es posible .

Hay muchas maneras agradables de hacer devoluciones de llamada en Objective-C, aquí hay dos que pueden ser útiles para usted:

  • Bloques : Definidos en context, elija cualquier recuento de arguments y types, posibles problemas con la memory también. Hay muchos resources sobre cómo usarlos.
  • performSelector : máximo 2 arguments de object, invocados usando:

     [target performSelector:selector withObject:data withObject:args]; 

Además, cuando necesito invocar un selector con 4 arguments, todavía no uso NSIvocation , sino que objc_msgSend directamente:

 id returnValue = objc_msgSend(target, selector, data, /* argument1, argument2, ... */); 

Sencillo.

Editar: con objc_msgSend , debe tener cuidado con el valor de retorno. Si su método devuelve un object, use lo anterior. Si devuelve un tipo primitivo, necesita lanzar el método objc_msgSend para que el comstackdor sepa lo que está sucediendo ( vea este enlace ). Aquí hay un ejemplo para un método que toma un argumento y devuelve un BOOL:

 // Cast the objc_msgSend function to a function named BOOLMsgSend which takes one argument and has a return type of BOOL. BOOL (*BOOLMsgSend)(id, SEL, id) = (typeof(BOOLMsgSend)) objc_msgSend; BOOL ret = BOOLMsgSend(target, selector, arg1); 

Si su método devuelve una estructura, las cosas son un poco más complicadas. Puede (pero no siempre) necesitará usar objc_msgSend_stret ; consulte aquí para get más información .

Editar: esta línea debe agregarse al código o Xcode se quejará:

 #import <objc/message.h> 

o

 @import ObjectiveC.message; 

Por lo general, debe considerar los bloques como una alternativa superior cuando sea posible (tuvieron éxito NSInvocation ).

En cuanto al valor de retorno, puede usar esto:

 CFTypeRef result = NULL; [invocation getReturnValue:&result]; NSString *returnValue = (__bridge NSString *)result; 

El problema subyacente aquí es que -getReturnValue: no devuelve un object de salida, en lo que respecta a ARC. Por lo tanto, es probable que las operaciones de recuento de references sean incorrectas (el comstackdor las agrega a usted en ARC), porque el -getReturnValue: es void* , no un object de salida (p NSObject** Ej. NSObject** ).