SudzC ARC版本 - objc_msgSend调用使用64位架构导致EXC_BAD_ACCESS [英] SudzC ARC version - objc_msgSend call causes EXC_BAD_ACCESS using 64-bit architecture
问题描述
编辑 - 我已将以下问题跟踪到64位与32位架构问题...请参阅我发布的答案,了解我如何解决
我使用 SudzC 为Web服务生成SOAP代码。他们为您提供了一个示例应用程序,我可以在设备和模拟器上成功使用它。
I've used SudzC to generate SOAP code for a web service. They supply you with a sample application, which I was able to use successfully, both on device and simulator.
然后我开始构建我的应用程序。我使用空白应用程序模板(启用了CoreData和ARC)将SudzC生成的文件导入到新的XCode项目中。
I then started building out my app. I imported the SudzC generated files into a new XCode project using the blank application template (with CoreData and ARC enabled).
我得到了第一个SOAP请求并运行 - 一切都在模拟器中工作 - 然后我去了我的第一个测试设备(运行iOS 7.02的iPhone 5S)。每次运行SOAP请求时,设备都会抛出 EXC_BAD_ACCESS
错误。
I got the first SOAP request up and running -- everything works in the simulator -- and then I went to do my first test on a device (iPhone 5S running iOS 7.02). The device throws an EXC_BAD_ACCESS
error every time the SOAP request is run.
我已将此跟踪到 SoapRequest.m
文件,特别是 connectionDidFinishLoading
方法。此方法使用 objc_msgSend
调用将SOAP响应数据发送回另一个类(在本例中为我的视图控制器)中的处理程序方法。以下是代码:
I've tracked this down to the SoapRequest.m
file, specifically the connectionDidFinishLoading
method. This method uses a objc_msgSend
call to send the SOAP response data back to a handler method in another class (in this case, my view controller). Here's the code:
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
指向我的View Controller, self.action
指向此方法:
So the line objc_msgSend(self.handler, self.action, output);
seems to be where my issue is. self.handler
is pointing to my View Controller, and self.action
points to this method:
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)值
并未从SoapRequest类中完成。我认为它已被ARC解除分配。我用 int
替换(id)值
测试了这个调用:
The re-entry to this method is where I crash out. It looks like the (id)value
is not making it over from the SoapRequest class. I assume it is getting deallocated by ARC. I've tested the call by replacing (id)value
with an int
:
objc_msgSend(self.handler,self.action,1);
和
- (void)findItemHandler:(int)value
这有效。假设问题是值变量过早被破坏,我尝试了一些事情来保持它。我向SoapRequest.m添加了一个属性:
This works. Assuming the issue is the value variable getting prematurely destroyed, I tried a few things to try and keep it retained. I added a property to SoapRequest.m:
@property(非原子,强)id值;
然后传递:
self.value = output;
objc_msgSend(self.handler, self.action, self.value);
同样的问题。我也尝试过与SoapRequest实例相同的东西......现在,我能够通过在视图控制器中创建属性并将该属性设置为值来解决该问题,但我真的很好奇如何使用原始代码。我回到了我从SudzC下载的示例应用程序,看看它是如何工作的,结果是ARC没有为该项目启用(!)。
Same problem. I also tried the same thing with the instance of SoapRequest...Now, I was able to work around the issue by creating a property in the view controller and setting that property to the value, but I am really curious how to fix this using the original code. I went back to the example app I downloaded from SudzC to see how that was working, and it turns out ARC is not enabled for that project (!).
可以有人告诉我:
1)如果我假设输出
被解除分配是正确的,导致处理程序方法引用坏内存地址?
1) If I am correct in my assumption that output
is being deallocated, causing the handler method to reference a bad memory address?
2)为什么这在模拟器上有效?我认为这是因为sim有更多的内存可用,所以它不像ARC解除分配那么激进......
2) Why this works on the simulator? I assume it is because the sim has a lot more memory available so it isn't as aggressive with ARC deallocations...
3)我怎么能解决这个问题,假设我想保留 objc_msgSend
来电?我想知道如何/为什么会发生这种情况
3) How I could fix this, assuming I want to keep the objc_msgSend
call? I want to learn how/why this is happening
4)如果SudzC的使用正确 objc_msgSend
,据我所知,除非在极少数情况下直接调用它是不好的做法?
4) If SudzC is correct in their usage here of objc_msgSend
, as I understand it is bad practice to call this directly except in rare circumstances?
谢谢!
推荐答案
好的 - 所以经过更多的研究和研究后,我终于意识到它可能是64位与32位问题。这是我升级到新Xcode后第一个应用的应用程序。我去了Build Settings并将架构从标准架构(包括64位)(armv7,armv7s,arm64)更改为标准架构(armv7,armv7s)。这解决了这个问题!
OK - so after more hair-pulling and research, it finally dawned on me that it might be a 64-bit vs 32-bit issue. This was the first app I was working on since upgrading to the new Xcode. I went to Build Settings and changed the Architectures from "Standard architectures (including 64-bit) (armv7, armv7s, arm64)" to "Standard architectures (armv7, armv7s)". This fixed the issue!
然后我回去研究了为什么会这样。我发现了这个Apple 64位转换指南: https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html
I then went back and researched why this was happening. I found this Apple 64-bit transition guide: https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaTouch64BitGuide/ConvertingYourAppto64-Bit/ConvertingYourAppto64-Bit.html
该文档提到以下内容:
使用方法函数的原型调度Objective-C消息
异常上面描述的转换规则是在
中调用objc_msgSend函数或发送消息的
Objective-C运行时中的任何其他类似函数。虽然
消息函数的原型具有可变形式,但Objective-C运行时调用的
方法函数不共享相同的原型。
Objective-C运行时直接调度到
实现该方法的函数,因此调用约定不匹配,如前面描述的
。因此,必须将objc_msgSend
函数强制转换为与所调用的方法函数匹配的原型。
Dispatch Objective-C Messages Using the Method Function’s Prototype An exception to the casting rule described above is when you are calling the objc_msgSend function or any other similar functions in the Objective-C runtime that send messages. Although the prototype for the message functions has a variadic form, the method function that is called by the Objective-C runtime does not share the same prototype. The Objective-C runtime directly dispatches to the function that implements the method, so the calling conventions are mismatched, as described previously. Therefore you must cast the objc_msgSend function to a prototype that matches the method function being called.
清单2-14显示了将消息分派到的正确表单使用低级消息函数的
对象。在此示例中,
doSomething:方法采用单个参数,并且没有
可变参数形式。它使用方法函数的原型
来强制转换objc_msgSend函数。请注意,方法函数始终将id
变量和选择器作为其前两个参数。在
objc_msgSend函数转换为函数指针之后,调用是通过相同的函数指针调度的
。
Listing 2-14 shows the proper form for dispatching a message to an object using the low-level message functions. In this example, the doSomething: method takes a single parameter and does not have a variadic form. It casts the objc_msgSend function using the prototype of the method function. Note that a method function always takes an id variable and a selector as its first two parameters. After the objc_msgSend function is cast to a function pointer, the call is dispatched through that same function pointer.
使用此信息,我更改了以下行:
Using this information, I changed the below line:
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);
一切正常!
希望这将有助于其他人......
Hopefully this will help someone else...
这篇关于SudzC ARC版本 - objc_msgSend调用使用64位架构导致EXC_BAD_ACCESS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!