SudzC ARC版本 – objc_msg发送调用导致使用64位架构的EXC_BAD_ACCESS

编辑 – 我已经跟踪下面的问题到64位和32位体系结构的问题…看到我已发布的答案我如何解决

我使用SudzC为Web服务生成SOAP代码。 他们提供了一个示例应用程序,我可以在设备和模拟器上成功使用。

然后我开始构build我的应用程序。 我使用空白的应用程序模板(启用了CoreData和ARC)将SudzC生成的文件导入到一个新的XCode项目中。

我得到了第一个SOAP请求,并运行 – 一切工作在模拟器 – 然后我去做一个设备(iPhone 5S运行iOS 7.02)的第一次testing。 每次运行SOAP请求时,设备都会引发EXC_BAD_ACCESS错误。

我跟踪到了SoapRequest.m文件,特别是connectionDidFinishLoading方法。 此方法使用objc_msgSend调用将SOAP响应数据发送回另一个类(本例中为视图控制器)中的处理程序方法。 代码如下:

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; } 

所以行objc_msgSend(self.handler, self.action, output); 似乎是我的问题在哪里。 self.handler指向我的视图控制器, self.action指向这个方法:

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); } 

重新进入这个方法是我崩溃的地方。 它看起来像(id)value没有从SoapRequest类中完成。 我认为它被ARC取消分配。 我已经通过用intreplace(id)valuetesting调用:

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

- (void)findItemHandler:(int)value

这工作。 假设问题是价值variables过早被破坏,我尝试了几件事情来保留它。 我添加了一个属性SoapRequest.m:

@property (nonatomic, strong) id value;

然后通过:

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

同样的问题。 我也尝试了与SoapRequest的实例相同的东西…现在,我能够通过在视图控制器中创build一个属性,并将该属性设置为值解决问题,但我真的很好奇如何解决这个问题原来的代码。 我回到我从SudzC下载的示例应用程序,看看这是如何工作的,事实certificateARC没有启用该项目(!)。

有人可以告诉我:

1)如果我是正确的在我的假设output被解除分配,导致处理方法引用一个错误的内存地址?

2)为什么这在模拟器上工作? 我认为这是因为SIM卡有更多的可用内存,所以它不像ARC释放那样具有攻击性。

3)如何我可以解决这个问题,假设我想保持objc_msgSend调用? 我想知道这是怎么发生的

4)如果SudzC在objc_msgSend的使用方面是正确的,据我所知直接调用它是一种不好的做法,除非在极less数情况下。

谢谢!

好的 – 所以在经过更多的调整和研究后,终于明白了它可能是一个64位和32位的问题。 这是自升级到新的Xcode以来我第一个应用程序。 我去build立设置和架构从“标准体系结构(包括64位)(armv7,armv7s,arm64)”改为“标准体系结构(armv7,armv7s)”。 这解决了这个问题!

然后我回去研究为什么会发生这种情况。 我发现这个苹果64位转换指南: https : //developer.apple.com/library/content/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html

该文件提到了以下内容:

使用方法函数的原型调度Objective-C消息上述的转换规则的例外是在Objective-C运行时中调用objc_msgSend函数或任何其他类似的函数来发送消息。 尽pipe消息函数的原型具有可变forms,但由Objective-C运行时调用的方法函数并不共享相同的原型。 Objective-C运行时直接调度到实现该方法的函数,所以调用约定是不匹配的,如前所述。 因此,您必须将objc_msgSend函数转换为与正在调用的方法函数相匹配的原型。

清单2-14显示了使用低级消息函数将消息分派给对象的正确forms。 在这个例子中,doSomething:方法只有一个参数,没有可变的forms。 它使用方法函数的原型来投射objc_msgSend函数。 注意一个方法函数总是把一个idvariables和一个select器作为前两个参数。 在将objc_msgSend函数强制转换为函数指针后,调用将通过相同的函数指针调用。

使用这个信息,我改变了下面的行:

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

至:

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

而所有的工作!

希望这会帮助别人…