This code works ok.
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser parse];
[parser release];
But if I use dispatch_async I will get EXC_BAD_ACCESS. But why?
parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
{
[parser parse];
});
[parser release];
Make sure that the parsers delegate is not being released prematurely. The parser will only hold a weak reference to the delegate and if the delegate is dealloc'ed before parsing is complete, you will get a bad access when the parser attempts to call it's delegate methods.
Actually the reason was [parser release] happens before [parser parse]. Once after dispatching the block into a queue (the block will be there waiting for execution), [parser release] will be invoked immediately. Keep in mind that there is an execution delay for the block when using dispatch_async.
This also explains why when you put [parser release] in the block and everything becomes fine.
Related
I have NSXMLParser problem, and i tried iOS8 NSXMLParser crash this topic, but i really did not get the solution.
I am creating another NXSMLParser delegate and setting its delegate in another class.
Could you please tell me what to do exactly, step by step? I am so confused.
Here is my code;
These lines of codes are inside the STXMLParser
STXMLParser2 *stXMLParser2 = [[STXMLParser2 alloc]init];
stXMLParser2.xmlParser = [[NSXMLParser alloc] initWithData:responseLoader.xmlData];
[stXMLParser2.xmlParser setDelegate:self];
[stXMLParser2.xmlParser setShouldResolveExternalEntities:YES];
[stXMLParser2.xmlParser parse];
You can try this code:
dispatch_queue_t reentrantAvoidanceQueue = dispatch_queue_create("reentrantAvoidanceQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(reentrantAvoidanceQueue, ^{
STXMLParser2 *stXMLParser2 = [[STXMLParser2 alloc]init];
stXMLParser2.xmlParser = [[NSXMLParser alloc] initWithData:responseLoader.xmlData];
[stXMLParser2.xmlParser setDelegate:self];
[stXMLParser2.xmlParser setShouldResolveExternalEntities:YES];
[stXMLParser2.xmlParser parse];
});
dispatch_sync(reentrantAvoidanceQueue, ^{ });
I was getting the same error and it turned out that the problem was due to calling a UI update in the func parserDidEndDocument(parser: NSXMLParser) which does not run on the main thread. After forcing the UI update in that function to run on the main queue, the problem was resolved.
I encountered the same problem recently but it turned out that I had an exception in one of my delegates (KVO problem) and once I fixed that the reentracy error went away. So it might be worth to look for something else if you don't have an obvious multithreading or multiinstance problem.
I send an update to my server every 5 seconds with a timer. In response I receive an XML from the server which I parse using NSXMLParser. MyParser is set as the delegate and implements the needed callbacks to parse the document (including - (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError).
most of the time the parsing is successful but every few minutes initWithContentsOfURL gets stuck for about a minute (almost exactly one minute avery time), then [parser parse] returns false and after that i keep getting successful parsing till the next failure.
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(parseDocument)
userInfo:nil
repeats:YES];
- (void) parseDocument {
dispatch_async(queue, ^{
NSLog(#"before NSXMLParser alloc");
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[[NSURL alloc] initWithString:#"https://secure.xxxxxxxxxxxxxxxxxxxxxx.dbm?xxxxxxxxxxxxxxxxxxxx"]];
NSLog(#"after NSXMLParser alloc");
MyParser *myParser = [[MyParser alloc] initParser:4];
[parser setDelegate:myParser];
BOOL result = [parser parse];
if (result) {
NSLog(#"Successful parse");
} else {
NSLog(#"Failed parse");
}
});
}
My question is how can I check why the parse (which fails) is taking about a minute and than returns false???
parseErrorOccurred is never called.
I checked the server side and everything is working and running as it should.
Any ideas????
Thanks in advance
EDIT:
As it turns out, [parser parse] is working fine, the problem occurs in initWithContentsOfURL , every few minutes this line takes one minute (instead of 1-2 second) than the parse returns false.
i checked server side if everything is ok within that minute and it looks fine.
also checked if parser is not nil, which it isn't.
any suggestions on how to check whats causing the problem every few minutes in this line: NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:[[NSURL alloc] initWithString:#"https://secure.xxxxxxx.dbm?xxxxxxxxxx"]];, will be very appreciated.
Thanks
Switch to an asynchronous fetch that explicitly alerts you upon a communications error: NSURLSession if under 7 or later, the appropriate class method of NSURLConnection if support for 6 is required. Either way you'll end up with an NSError, an NSData and an NSURLResponse, which you can cast to NSHTTPURLResponse to check for a 200 response code.
If there's no error, a valid response code and greater-than-zero length data, proceed to hand the data to an NSXMLParser.
EDIT: so, I'm on a real computer now (rather than an iPad) and therefore can add that I'd gravitate towards code like:
[[[NSURLSession sharedSession]
dataTaskWithURL:[NSURL URLWithString:#"https://secure.xxxxxxxxxxxxxxxxxxxxxx.dbm?xxxxxxxxxxxxxxxxxxxx"]
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error)
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if(httpResponse.statusCode != 200 || error || ![data length])
{
NSLog(#"Some error condition");
return;
}
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:myParser];
BOOL result = [parser parse];
if (result) {
NSLog(#"Successful parse");
} else {
NSLog(#"Failed parse");
}
}] resume];
I've favoured NSURLSession even though I'm just then telling the returned task to resume and never using any of the improved functionality over NSURLConnection's asynchronous mechanism, such as cancellation, because it is anecdotally much better at caching and because it'll automatically ensure an appropriate place is found to accumulate results and ultimately dispatch the completion handler asynchronously. So that's a few lines saved.
I am using the class XMLReader to parse some XML from a URL. The XML is successfully parsed sometimes, and sometimes I get:
Error Domain=NSXMLParserErrorDomain Code=4 "The operation couldn’t be completed. (NSXMLParserErrorDomain error 4.)"
The parse is usually successful the first time I run it, after changing something, and it fails after that until I change something else. For example, in the code below, I tried commenting out the [parser release] line, and it parsed successfully. Then I ran it again and back to error code 4.
I log the same input data every time, success or fail.
Any ideas what is going wrong here? I can paste in more code if that would help, but I have isolated the error to be within the NSXMLParser parse method (called in the code below), because it always receives the same data.
Thanks!
edit: I know that error code 4 is an empty document error. But I know my NSData is not empty. So there is something else happening here
- (NSDictionary *)objectWithData:(NSData *)data
{
//data always makes it here, the same data gets logged regardless of parse success
//NSLog(#"%#",data);
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
[parser setShouldResolveExternalEntities:NO];
BOOL success = [parser parse];
[parser release];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
The issue isn't whether data is correct at the beginning of your method objectWithData; it is whether data is correct through out the parse method computation. You should check that integrity of data after the parser completes.
In all likelihood, data is returned by the XMLReader but you are not retaining it properly. Occasionally the computationally intensive parser forces a garbage collection (or pool reclamation) and data gets corrupted.
Just do a [data retain] at the start of your objectwithData method; the problem will then disappear.
I have a memory leak with below code.
Where self.firstURLConn is #property(nonatomic, retain).
NSMutableURLRequest* req = [[NSMutableURLRequest alloc] initWithURL:urlcachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60.0f];
self.firstURLConn = [[[NSURLConnection alloc] initWithRequest:req delegate:self] autorelease];
[req release];//memory leak here and with self.firstURLConn
Why memory is leaking here
Did you ever run Product > Analyze yet? If you did please show us the memory leaking issue log.
They will not only tell the line of your code that have memory leak issue but show the step of your code that cause the problem.
I suggest you to use Instrument tool with leaking tool. It will show the in-depth information about your code problem.
EDIT: req variable miss autorelease. because of req has been retain 2 time. change the code like this
NSMutableURLRequest* req = [[[NSMutableURLRequest alloc] initWithURL:urlcachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60.0f] autorelease];
self.firstURLConn = [[[NSURLConnection alloc] initWithRequest:req delegate:self] autorelease];
[req release];//memory leak here and with self.firstURLConn
Because of your firstURLConn has declared in retain properties. So,the dealloc method, you should set nil to this properties
- (void)dealloc
{
self.firstURLConn = nil;
[super dealloc];
}
firstURLConn will retain your req, req will be only deallocated when firstURLConn will release req.
Since the delegate method returns asynchronously (sometime in the future) you must release the NSURLConnection inside of the delegate method. Remove the autorelease and add a release in the completed and failed delegate methods.
Recently i decided to use the SKPSMTPMessage Class for transferring images to a server.
I subclassed the NSOperation Class, implemented the funktionality and added it to a NSOperationQueue, because i don't want my app to be blocked and the user can't do anything while its uploading the image. This usually occur, when i use the GSM network and it lasts a long time, until it the image is being sent. (By the way, i don't want to do any compressions on the image)
- (void)main {
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail = [[_from copy] autorelease];
testMsg.toEmail = [[_to copy] autorelease];
testMsg.relayHost = [[_relayHost copy] autorelease];
testMsg.subject = [[_subject copy] autorelease];
testMsg.delegate = self.delegate;
testMsg.parts = [[_mailParts copy] autorelease];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];}
To be clear, i release the obect in the delegate i assign to the SKPSMTMessage instance. So it's not causing any leaks.
The Problem is, that i have to use performSelectorOnMainThread: because otherwise its not working. It stops right after
C: Attempting to connect to server at: mail.example.com:25
I've found this post here, that describes pretty much the same problem i'm currently facing, but i can't figure out, how its going to work.
The solution they described was, that they used to run the program in another thread.
[NSThread detachNewThreadSelector:#selector(launchJobWithJob:) toTarget:self withObject:jobDescription];
[[NSRunLoop currentRunLoop] run];
But when i do this without using the NSOperation subclass, it causes me this error:
_NSAutoreleaseNoPool(): Object 0x18a140 of class NSCFString autoreleased with no pool in place - just leaking
but still not working. It again only prints this "C: Attempting to connect to server at: mail.example.com:25".
Can anyone please help?
EDIT
In the Subclass NSOperation I now use instead of
[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];
that code.
[testMsg send];
[[NSRunLoop currentRunLoop] run];
It helps me getting across the freezing problem, but the reliability of the message being sent is not given.
It works now! I use following code in the NSOperation Subclass (btw: My subclasses name is SMTPSendOperation):
- (void)main
{
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail = [[_from copy] autorelease];
testMsg.toEmail = [[_to copy] autorelease];
testMsg.relayHost = [[_relayHost copy] autorelease];
testMsg.subject = [[_subject copy] autorelease];
testMsg.delegate = self.delegate;
testMsg.parts = [[_mailParts copy] autorelease];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];
//[NSThread detachNewThreadSelector:#selector(send) toTarget:<#(id)#> withObject:<#(id)#>]
[testMsg send];
[[NSRunLoop currentRunLoop] run];
}
Anyhow, i'll have to tell the user to be patient until the mail is being sent successfully or failed. If anyone has any idea, how i can use the thread to be run more efficient, i'll really appreciate that!