Изменить - я отслеживал проблему ниже для проблемы с 64-разрядной и 32-разрядной архитектурой... см. мой ответ на вопрос, как я разрешил
Я использовал SudzC для создания SOAP-кода для веб-службы. Они снабжают вас примером приложения, которое я смог успешно использовать, как на устройстве, так и на симуляторе.
Затем я начал создавать свое приложение. Я импортировал созданные файлы SudzC в новый проект XCode, используя пустой шаблон приложения (с поддержкой CoreData и ARC).
Я получил первый запрос SOAP и все работает в симуляторе, а затем я начал выполнять свой первый тест на устройстве (iPhone 5S под управлением iOS 7.02). Устройство выдает ошибку EXC_BAD_ACCESS
каждый раз, когда выполняется запрос SOAP.
Я проследил это до файла 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. Я протестировал вызов, заменив (id)value
на int
:
objc_msgSend(self.handler, self.action, 1);
и
- (void)findItemHandler:(int)value
Это работает. Предполагая, что проблема заключается в том, что переменная значений, преждевременно разрушенная, я попробовал несколько вещей, чтобы попытаться сохранить ее. Я добавил свойство SoapRequest.m:
@property (nonatomic, strong) id value;
Затем передано это:
self.value = output;
objc_msgSend(self.handler, self.action, self.value);
Такая же проблема. Я также пробовал то же самое с экземпляром SoapRequest... Теперь я смог обойти проблему, создав свойство в контроллере представления и установив это свойство в значение, но мне действительно интересно, как исправить это, используя исходный код. Я вернулся к примеру приложения, загруженного из SudzC, чтобы посмотреть, как это работает, и оказывается, что ARC не включен для этого проекта (!).
Может ли кто-нибудь сказать мне:
1) Если я прав в своем предположении, что output
освобождается, что приводит к тому, что метод обработчика ссылается на неправильный адрес памяти?
2) Почему это работает на симуляторе? Я предполагаю, что это связано с тем, что у сима намного больше доступной памяти, поэтому она не так агрессивно с отключением ARC...
3) Как я могу это исправить, если я хочу сохранить вызов objc_msgSend
? Я хочу узнать, как/почему это происходит.
4) Если SudzC правилен в использовании здесь objc_msgSend
, так как я понимаю, что это плохая практика называть это напрямую, за исключением редких случаев?
Спасибо!