titulos font color ios objective-c automatic-ref-counting sudzc

ios - font - title ionic 3



VersiĆ³n de SudzC ARC-objc_msgEnviar llamada EXC_BAD_ACCESS usando una arquitectura de 64 bits (1)

Editar: he rastreado el problema de abajo a un problema de arquitectura de 64 bits frente a 32 bits ... vea mi respuesta publicada para la resolución

He usado SudzC para generar código SOAP para un servicio web. Le proporcionan una aplicación de muestra, que pude usar con éxito, tanto en el dispositivo como en el simulador.

Entonces empecé a construir mi aplicación. Importé los archivos generados de SudzC en un nuevo proyecto XCode usando la plantilla de aplicación en blanco (con CoreData y ARC habilitados).

Tengo la primera solicitud de SOAP en funcionamiento, todo funciona en el simulador, y luego fui a hacer mi primera prueba en un dispositivo (iPhone 5S con iOS 7.02). El dispositivo lanza un error EXC_BAD_ACCESS cada vez que se ejecuta la solicitud SOAP.

He rastreado esto hasta el archivo SoapRequest.m , específicamente el método connectionDidFinishLoading . Este método utiliza una llamada objc_msgSend para enviar los datos de respuesta de SOAP a un método de controlador en otra clase (en este caso, mi controlador de vista). Aquí está el código:

SoapRequest.m:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSError* error; if(self.logging == YES) { NSString* response = [[NSString alloc] initWithData: self.receivedData encoding: NSUTF8StringEncoding]; NSLog(@"%@", response); } CXMLDocument* doc = [[CXMLDocument alloc] initWithData: self.receivedData options: 0 error: &error]; if(doc == nil) { [self handleError:error]; return; } id output = nil; SoapFault* fault = [SoapFault faultWithXMLDocument: doc]; if([fault hasFault]) { if(self.action == nil) { [self handleFault: fault]; } else { if(self.handler != nil && [self.handler respondsToSelector: self.action]) { objc_msgSend(self.handler, self.action, fault); } else { NSLog(@"SOAP Fault: %@", fault); } } } else { CXMLNode* element = [[Soap getNode: [doc rootElement] withName: @"Body"] childAtIndex:0]; if(deserializeTo == nil) { output = [Soap deserialize:element]; } else { if([deserializeTo respondsToSelector: @selector(initWithNode:)]) { element = [element childAtIndex:0]; output = [deserializeTo initWithNode: element]; } else { NSString* value = [[[element childAtIndex:0] childAtIndex:0] stringValue]; output = [Soap convert: value toType: deserializeTo]; } } if(self.action == nil) { self.action = @selector(onload:); } if(self.handler != nil && [self.handler respondsToSelector: self.action]) { objc_msgSend(self.handler, self.action, output); } else if(self.defaultHandler != nil && [self.defaultHandler respondsToSelector:@selector(onload:)]) { [self.defaultHandler onload:output]; } } conn = nil; }

Así que la línea objc_msgSend(self.handler, self.action, output); Parece ser donde está mi problema. self.handler apunta a mi controlador de vista, y self.action apunta a este método:

TareasViewController.m:

- (void) findItemHandler: (id) value { // Handle errors if([value isKindOfClass:[NSError class]]) { NSLog(@"%@", value); return; } // Handle faults if([value isKindOfClass:[SoapFault class]]) { NSLog(@"%@", value); return; } // Do something with the id result NSLog(@"FindItem returned the value: %@", value); }

El reingreso a este método es donde me caigo. Parece que el (id)value no lo está superando de la clase SoapRequest. Supongo que está siendo desasignado por ARC. He probado la llamada al reemplazar el (id)value con un int :

objc_msgSend(self.handler, self.action, 1);

y

- (void)findItemHandler:(int)value

Esto funciona. Suponiendo que el problema es que la variable de valor se destruye prematuramente, intenté algunas cosas para intentar mantenerla retenida. Agregué una propiedad a SoapRequest.m:

@property (nonatomic, strong) id value;

Luego pasó eso:

self.value = output; objc_msgSend(self.handler, self.action, self.value);

El mismo problema. También intenté lo mismo con la instancia de SoapRequest ... Ahora, pude solucionar el problema creando una propiedad en el controlador de vista y configurando esa propiedad al valor, pero estoy realmente curioso de cómo solucionar este problema usando El código original. Regresé a la aplicación de ejemplo que descargué de SudzC para ver cómo funcionaba, y resulta que ARC no está habilitado para ese proyecto (!).

Alguien me puede decir:

1) Si estoy en lo cierto al suponer que la output se está desasignando, ¿el método del controlador hace referencia a una dirección de memoria incorrecta?

2) ¿Por qué funciona esto en el simulador? Supongo que es porque el simulador tiene mucha más memoria disponible, por lo que no es tan agresivo con las desasignaciones ARC ...

3) ¿Cómo podría solucionar esto, asumiendo que quiero mantener la llamada objc_msgSend ? Quiero saber cómo / por qué sucede esto.

4) Si SudzC es correcto en su uso aquí de objc_msgSend , entiendo que es una mala práctica llamar a esto directamente, excepto en circunstancias excepcionales.

¡Gracias!


Bien, así que después de más esfuerzo e investigación, finalmente me di cuenta de que podría tratarse de un problema de 64 bits frente a 32 bits. Esta fue la primera aplicación en la que estuve trabajando desde que me actualicé al nuevo Xcode. Fui a Build Settings y cambié las arquitecturas de "Arquitecturas estándar (incluyendo 64 bits) (armv7, armv7s, arm64)" a "Arquitecturas estándar (armv7, armv7s)". Esto solucionó el problema!

Luego volví e investigué por qué sucedía esto. Encontré esta guía de transición de Apple de 64 bits: https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html

El documento menciona lo siguiente:

Enviar mensajes de Objective-C utilizando el prototipo de la función de método Una excepción a la regla de conversión descrita anteriormente es cuando llama a la función objc_msgSend o a cualquier otra función similar en el tiempo de ejecución de Objective-C que envía mensajes. Aunque el prototipo para las funciones de mensaje tiene una forma variada, la función de método que llama el tiempo de ejecución de Objective-C no comparte el mismo prototipo. El tiempo de ejecución de Objective-C se envía directamente a la función que implementa el método, por lo que las convenciones de llamada no coinciden, como se describió anteriormente. Por lo tanto, debe convertir la función objc_msgSend a un prototipo que coincida con la función del método que se está llamando.

El Listado 2-14 muestra la forma adecuada para enviar un mensaje a un objeto utilizando las funciones de mensajes de bajo nivel. En este ejemplo, el método doSomething: toma un solo parámetro y no tiene una forma variable. Ejecuta la función objc_msgSend utilizando el prototipo de la función de método. Tenga en cuenta que una función de método siempre toma una variable de identificación y un selector como sus dos primeros parámetros. Después de que la función objc_msgSend se convierte en un puntero de función, la llamada se envía a través del mismo puntero de función.

Usando esta información, cambié la línea de abajo:

objc_msgSend(self.handler, self.action, self.value);

a:

id (*response)(id, SEL, id) = (id (*)(id, SEL, id)) objc_msgSend; response(self.handler, self.action, output);

¡Y todo está funcionando!

Esperemos que esto ayude a alguien más ...