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.
Related
I have the following code,
NSString *string = "Some value";
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
self.parser = [[NSXMLParser alloc] initWithData:data];
self.parser.delegate = self;
[self.parser parse];
After I call "parse" method it immediately calls the delegate parseErrorOccurred and then calls delegate parserDidEndDocument.
I want only one method to be called.
Thanks in advance.
That delegate method is called when there is a fatal error, so there is an issue with the XML you are parsing.
The method has an NSError parameter, and that should give you a clue as to what is wrong with the XML.
You mean when an error occurs in parsing, you want only parseErrorOccurred() called and parserDidEndDocument() not called?
One possible solution: write parser.abortParsing() in parseErrorEccurred(). I'm not sure this would actually work, if it fails, try the solution below:
Use a flag in your class that wrap the parser. Set the flag to true in parseErrorEccurred(), then check this flag in parserDidEndDocument() (if it's false then execute other code).
Hope this will solve your problem. :)
Firstly, as I see many others seem to announce when they ask these types of questions, I'm a beginner to Objective C. I've come from a strong PHP background, so I do understand most programming concepts, and have been learning Obj C on and off for the past 12 months.
One of my first projects to get my feet wet with iOS Objective C was to integrate with the Magento SOAP API. Probably not the easiest thing to begin with, but nonetheless it's a good challenge.
I'm currently trying to integrate XMLReader (https://github.com/amarcadet/XMLReader). But XCode keeps throwing me an error:
ARC Semantic Issue: No known class method for selector 'dictionaryForNSXMLParser:'
Which refers to the following code:
NSDictionary *dict = [XMLReader dictionaryForNSXMLParser:parser];
I found some advice from another question:
How can you use AFNetworking or STHTTPRequest to make a request of a SOAP web service?
I've reverted my code to mirror exactly the examples provided in the question, so I've been wracking my brain to work this error out, but to no avail.
Any help is much appreciated, and I apologise if this is something stupid which I have overlooked. I've tried scouring google for similar issues, but they all seem to be leading to class methods being called on an instance, etc.
I was trying that code myself just a minute ago.
You have to add in XMLReader.h:
+(NSDictionary*)dictionaryForNSXMLParser:(NSXMLParser*)parser error:(NSError **)error;
Then in the XMLReader.m these two:
+ (NSDictionary *)dictionaryForNSXMLParser:(NSXMLParser *)xmlParser error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithNSXMLParser:xmlParser options:0];
return rootDictionary;
}
- (NSDictionary *)objectWithNSXMLParser:(NSXMLParser *)xmlParser options:(XMLReaderOptions)options
{
// Clear out any old data
self.dictionaryStack = [[NSMutableArray alloc] init];
self.textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[self.dictionaryStack addObject:[NSMutableDictionary dictionary]];
[xmlParser setShouldProcessNamespaces:(options & XMLReaderOptionsProcessNamespaces)];
[xmlParser setShouldReportNamespacePrefixes:(options & XMLReaderOptionsReportNamespacePrefixes)];
[xmlParser setShouldResolveExternalEntities:(options & XMLReaderOptionsResolveExternalEntities)];
xmlParser.delegate = self;
BOOL success = [xmlParser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [self.dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
After that remember to import XMLReader.h and use the method. Notice that it has the error handling in it so if you copy pasted the code it's not same.
[XMLReader dictionaryForNSXMLParser:parser error:nil];
If it still doesn't work try cleaning the project with CMD+SHIFT+K and building it again.
I'm using the following code to run a function that parses my xml files...
dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue,^ { [self updateFromXMLFile:#"http://path/to/file.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:#"http://path/to/file1.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:#"http://path/to/file2.xml"]; } );
dispatch_async(queue,^ { [self updateFromXMLFile:#"http://path/to/file3.xml"]; } );
dispatch_barrier_async(queue,^ {
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdated];
});
});
Here below is the function updateFromXMLFile:
- (BOOL) updateFromXMLFile:(NSString *)pathToFile {
NSURL *url = [[NSURL alloc] initWithString:pathToFile];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
parser.managedObjectContext = self.managedObjectContext;
[xmlParser setDelegate: parser];
BOOL success = [xmlParser parse];
if(success)
return TRUE;
else
return FALSE;
}
The problem I'm coming across is this error message: ***Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0xc675e10> was mutated while being enumerated.'
I'm guessing that it has something to do with all the processes messing with my ManagedObjectContext at the same time. I'm not sure how to handle that though. Any ideas? Thanks!
You need to show the code for how your XMLParser handles its delegate responsibilities, because that's what's being called.
Now, you are parsing multiple files at the same time, from within separate threads. They are all calling into different XMLParser objects, but each of those is using the same managed object context (MOC).
If you are using the default MOC setup, you need to reconvene all those MOC calls to the main thread (if that is, indeed where you first created your context). If you use confinement, and your MOC is not created on the main thread, then you are giving yourself more trouble.
However, it's a pretty simple fix. In that parser delegate method, whenever you use the managed object context, enclose it in a call to dispatch that part on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
// Put your code that accesses the MOC in here.
});
Now, for doing bulk downloads into Core Data, you are probably better off using one of two approaches.
Create a new MOC, attached directly to the persistent store coordinator, and do all your saving there. Your main MOC needs to observe Save notifications and merge those changes.
Or, make the new MOC be a child of your main MOC, and save directly into it, then it can save.
If, however, your managed object context is of either NSMainQueueConcurrencyType or NSPrivateQueueConcurrency type (only way to do second option above), then you can just use its perfromBlock method...
[managedObjectContext performBlock:^{
// Do your MOC stuff in here
}];
The bottom line is that a MOC can be created with one of three concurrency types. If it is NSConfinementConcurrencyType, then you must not touch it outside the thread in which it was created. If it is NSMainQueueConcurrencyType, you have to use performBlock* or only touch it in the main thread. If it is NSPrivateConcurrencyType, you must use performBlock*.
The problem is you cannot use a concurrent queue to interface to a MOC, you have to use a serial one. Change your queue creation to
dispatch_queue_t queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_SERIAL);
and all will be well. Again, the reason for doing this is that the final XMLParser objects are concurrently trying to interact with the moc.
Your options are dictated by what XMLParser (or any class) is doing. If a class does its heavy lifting BEFORE it interacts with the MOC, or it downloads data from the web, then you would gain something by concurrency.
What I would suggest you do is download the data you need from the URLS, then serially work with the MOC. You must interact with the MOC on a single thread (in iOS) but that does NOT have to be the mainThread - it can be any thread as long as its just the one. This means that if you create your own serial queue, everything has to be done on that queue including creating the MOC!
Back to your code. Lets assume that you do all Core Data work on the mainQueue (for now). The solution is change your code to this:
dispatch_async(dispatch_get_global_queue(0,0) {
NSURL *url = [[NSURL alloc] initWithString:pathToFile];
NSData *data = [NSData dataWithContentsOfURL:url];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData:data];
XMLParser *parser = [[XMLParser alloc] initXMLParser];
parser.managedObjectContext = self.managedObjectContext;
[xmlParser setDelegate: parser];
dispatch_async(dispatch_get_main_queue(), {
BOOL success = [xmlParser parse];
// need some means to associate success/failure with the URL
} );
You do as much as you can concurrently, the serialize when hitting the MOC. The only difference when you use a serial queue for the MOC is that your would message that queue instead of the mainQueue.
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.
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!