SudzC ARC version - a chamada objc_msgSend causa o EXC_BAD_ACCESS usando a arquitetura de 64 bits
Editar - acompanhei o problema abaixo para um problema de arquitetura de 64 bits versus 32 bits ... veja minha resposta publicada sobre como resolvi
Eu useiSudzC para gerar código SOAP para um serviço da web. Eles fornecem a você um aplicativo de amostra, que eu pude usar com sucesso, tanto no dispositivo quanto no simulador.
Comecei então a construir meu aplicativo. Eu importei os arquivos gerados do SudzC para um novo projeto XCode usando o modelo de aplicativo em branco (com CoreData e ARC ativados).
Eu tenho o primeiro pedido SOAP instalado e funcionando - tudo funciona no simulador - e então eu fui fazer meu primeiro teste em um dispositivo (iPhone 5S rodando iOS 7.02). O dispositivo lança umEXC_BAD_ACCESS
erro toda vez que a solicitação SOAP é executada.
Eu rastreei isso até oSoapRequest.m
arquivo, especificamente oconnectionDidFinishLoading
método. Este método usa umobjc_msgSend
chamar para enviar os dados de resposta SOAP de volta para um método manipulador em outra classe (neste caso, meu controlador de visualização). Aqui está o 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;
}
Então a linhaobjc_msgSend(self.handler, self.action, output);
parece ser onde está o meu problema.self.handler
está apontando para o meu View Controller, eself.action
aponta para este método:
TasksViewController.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);
}
A reentrada nesse método é onde eu falo. Parece que o(id)value
não está fazendo isso da classe SoapRequest. Eu suponho que ele está sendo desalocado pelo ARC. Eu testei a chamada substituindo(id)value
com umint
:
objc_msgSend(self.handler, self.action, 1);
e
- (void)findItemHandler:(int)value
Isso funciona. Assumindo que o problema é a variável de valor que foi prematuramente destruída, tentei algumas coisas para tentar mantê-la preservada. Eu adicionei uma propriedade ao SoapRequest.m:
@property (nonatomic, strong) id value;
Então passou isso:
self.value = output;
objc_msgSend(self.handler, self.action, self.value);
Mesmo problema. Eu também tentei a mesma coisa com a instância do SoapRequest ... Agora, fui capaz de contornar o problema criando uma propriedade no controlador de exibição e definindo essa propriedade para o valor, mas estou realmente curioso para saber como corrigir isso usando o código original. Voltei para o aplicativo de exemplo que baixei da SudzC para ver como isso estava funcionando, e acontece que o ARC não está habilitado para esse projeto (!).
Alguém pode me dizer:
1) Se estou correto em minha suposição de queoutput
está sendo desalocado, fazendo com que o método do manipulador faça referência a um endereço de memória incorreto?
2) Por que isso funciona no simulador? Eu presumo que seja porque o sim tem muito mais memória disponível, então não é tão agressivo com as desalocações do ARC ...
3) Como eu poderia consertar isso, supondo que eu queira manter oobjc_msgSend
ligar? Eu quero aprender como / porque isso está acontecendo
4) Se a SudzC estiver correta em seu uso aqui deobjc_msgSend
, como eu entendo, é uma má prática chamar isso diretamente, exceto em raras circunstâncias?
Obrigado!