exc_bad_access (code=2, address=0x20) in xmlParser - iphone

I am using xml parser in my application. When i run my application for 10 to 15 time its works fine but suddenly its giving me the bad_access with above codes. My xml parser code as follow:
-(BOOL)getTheServerStatus:(NSData *)webData
{
if (webData==NULL)
{
return FALSE;
}
parser=[[NSXMLParser alloc]initWithData:webData];
[parser setDelegate:self];
[self performSelectorOnMainThread:#selector(parseData:)
withObject:webData
waitUntilDone:YES];
if([strVal isEqualToString:#"ok"])
{
return TRUE;
}
else
{
return FALSE;
}
}
- (void)parseData:(NSData *)webData
{
if(webData==NULL)
{
NSLog(#"web data is NULL");
}
[parser parse];
}
I am using automatic reference counting .So what is the issue with my code?

I assume that getTheServerStatus is getting called on a thread which is NOT the main thread. Yet you do the parsing on the main thread. Is it possible a second thread us clobbering the parser?
EDIT: code changed
So what you should do is NOT block in getTheServerStatus:, but break your problem into two parts. The first is you want to get the status - so you are going to dispatch a block to do that work for you. While that is going on you can throw up a spinner, or just disable some of your UI. That is a design decision of course. When the background thread looking for status is done, it will message you back on the main thread with the result, and you can take whatever action you want then. I just posted an answer to a similar question that has even more code, which you might find helpful.
{ // ivars
NSXMLParser *parser; // so you can send it abortParsing to cancel the background block
}
-(void)getTheServerStatus:(NSData *)webData
{
if (webData==nil) // nil is for objects, NULL for pointers
{
dispatch_async(dispatch_get_main_queue(), ^{ [self parseResult:NO]; } );
}
parser=[[NSXMLParser alloc]initWithData:webData];
[parser setDelegate:self];
dispatch_async(dispatch_get_global_queue(0,0), ^
{
BOOL ret = [parser parse];
parser = nil;
if(ret == YES) {
ret = [strVal isEqualToString:#"ok"]; // EDIT
}
dispatch_async(dispatch_get_main_queue(), ^{ [self parseResult:ret]; } );
} );
}
-(void)parserResult:(BOOL)retCode
{
// now on main thread...
if(retCode == YES) ....
else .....
}

Related

iOS8 NSXMLParser crash

I had a crash in NSXMLParser
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'NSXMLParser does not support reentrant parsing.'
Here is my code
NSString *wrappedSnippet = [NSString stringWithFormat:#"<html>%#</html>", self.snippet];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:[wrappedSnippet dataUsingEncoding:NSUTF8StringEncoding]];
[parser setDelegate:self];
[parser parse];
app crashes on the last line.
Note, that everything works perfect on iOS7!
iOS8 throws an exception that previous versions caught and handled in the background. see manual As from ios 5 NSXMLParser is thread safe but not reentrant! make sure you aren't calling parse from your NSXMLParser delegate. "Self" in your case.
dispatch_queue_t reentrantAvoidanceQueue = dispatch_queue_create("reentrantAvoidanceQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(reentrantAvoidanceQueue, ^{
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:xml];
[parser setDelegate:self];
if (![parser parse]) {
NSLog(#"There was an error=%# parsing the xml. with data %#", [parser parserError], [[NSString alloc] initWithData:xml encoding: NSASCIIStringEncoding]);
}
[parser release];
});
dispatch_sync(reentrantAvoidanceQueue, ^{ });
Replace your code with above lines, Hope it helps you!
I resolved my problem by dispatching parser in background queue!
NSXMLParser is now threadsafe. However, it is not reentrant on a given thread; don't call -parse on an NSXMLParser from within a delegate callback of another NSXMLParser.
- (void)parseWithCompletion:(ParserHandler)handler {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
self.handler = handler;
[self parse];
});
}
- (void)parserDidEndDocument:(NSXMLParser *)parser {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.handler) {
self.handler(YES, self.dictionary, nil);
self.handler = nil;
}
});
}
In this issue, that means you could re-call [NSXMLParser parse] function in anything of his delegate.
Sometimes you may call [parser parse] in parserDidEndDocument:
But it will notify you that this's a reentrant error!
So, the solution is, either you could [parser parser] in different queue,
for example, you could do it via calling dispatch_async(dispatch_get_main_queue(), ^{ do in block});
or, you need to adjust your call flow,
make sure that you won't call parse function in delegate.
I had the same problem and wrote a subclass based upon NSXMLParser which handles the case:
class SynchronizedXMLParser: NSXMLParser
{
// shared queue
private static let _serialQueue: NSOperationQueue = {
let queue = NSOperationQueue()
queue.qualityOfService = NSQualityOfService.UserInitiated
// making it serial on purpose in order to avoid
// the "reentrant parsing" issue
queue.maxConcurrentOperationCount = 1
return queue
}()
// instance level convenience accessor
private var _serialQueue: NSOperationQueue
{
get
{
return self.dynamicType._serialQueue
}
}
private weak var _associatedParsingTask: NSBlockOperation?
deinit
{
_associatedParsingTask?.cancel()
}
//MARK: - Overridden
required override init(data: NSData)
{
super.init(data: data)
}
// still unsafe to call within the delegate callbacks
override func parse() -> Bool
{
var parsingResult = false
if (_associatedParsingTask == nil)
{
let parsingTask = NSBlockOperation(block: {() -> Void in
parsingResult = super.parse()
})
_associatedParsingTask = parsingTask
// making it synchronous in order to return the result
// of the super's parse call
_serialQueue.addOperations([parsingTask], waitUntilFinished: true)
}
return parsingResult
}
override func abortParsing()
{
if let parsingTask = _associatedParsingTask
{
parsingTask.cancel()
_associatedParsingTask = nil
}
super.abortParsing()
}
// MARK: - Introduced
// safe to use everywhere as it doesn't force the calling thread to wait until this me
thod returns
func parse(completion completion:(Bool) -> Void) -> Void
{
var parsingResult = false
if (_associatedParsingTask == nil)
{
let parsingTask = NSBlockOperation(block: {() -> Void in
parsingResult = super.parse()
})
parsingTask.completionBlock = { () -> Void in
completion(parsingResult)
}
_associatedParsingTask = parsingTask
// making it synchronous in order to return the result
// of the super's parse call
_serialQueue.addOperation(parsingTask)
}
}
}
P.S. The idea is pretty much the same as what #CrimeZone suggested.

Detect if a dispatch_async method is still running

I have a loadImages method
- (void)loadImages {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//this method loads images from a url, processes them
//and then adds them to a property of its view controller
//#property (nonatomic, strong) NSMutableArray *storedImages;
});
}
When a button is clicked a view enters the screen, and all the images that currently exist in _storedImages are displayed
- (void)displayImages {
for (NSString *link in _storedImages) {
//displayImages
}
}
The problem with this setup is, that if the user clicks the button before all the images are loaded, not all the images are presented on the screen.
Hence, I would like to display an SVProgressHUD if the button is clicked, and the loadImages dispatch_async method is still running.
So, how do I keep track of when this dispatch_async is completed? Because if I know this, then I can display an SVProgressHUD until it is completed.
On a side note, if you know how to load/display the images dynamically that info would be helpful too, i.e. you click the button and then as you see the current images, more images are downloaded and displayed
Thank you from a first time iOS developer!
Ok I found a solution but it is incredibly inefficient, I am sure there's a better way to do this
1. Keep a boolean property doneLoadingImages which is set to NO
2. After the dispatch method finishes, set it to YES
3. In the display images method, have a while (self.doneLoadingImages == NO)
//display progress HUD until all the images a loaded
Keep in mind that NSMutableArray is not thread-safe. You must ensure that you don't try to access it from two threads at once.
Using a boolean to track whether you're still loading images is fine. Make loadImages look like this:
- (void)loadImages {
self.doneLoadingImages = NO;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
UIImage *image = [self bg_getNextImage];
if (!image)
break;
dispatch_async(dispatch_get_main_queue(), ^{
[self addImage:image];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
[self didFinishLoadingImages];
});
});
}
So we send ourselves addImage: on the main queue for each image. The addImage: method will only be called on the main thread, so it can safely access storedImages:
- (void)addImage:(UIImage *)image {
[self.storedImages addObject:image];
if (storedImagesViewIsVisible) {
[self updateStoredImagesViewWithImage:image];
}
}
We send ourselves didFinishLoadingImages when we've loaded all the images. Here, we can update the doneLoadingImages flag, and hide the progress HUD if necessary:
- (void)didFinishLoadingImages {
self.doneLoadingImages = YES;
if (storedImagesViewIsVisible) {
[self hideProgressHUD];
}
}
Your button action can then check the doneLoadingImages property:
- (IBAction)displayImagesButtonWasTapped:(id)sender {
if (!storedImagesViewIsVisible) {
[self showStoredImagesView];
if (!self.doneLoadingImages) {
[self showProgressHUD];
}
}
}
What I usually do with this kind of problem is basically the following (rough sketch):
- (void)downloadImages:(NSArray*)arrayOfImages{
if([arrayOfImages count] != 0)
{
NSString *urlForImage = [arrayOfImages objectAtIndex:0];
// Start downloading the image
// Image has been downloaded
arrayOfImages = [arrayOfImages removeObjectAtIndex:0];
// Ok let's get the next ones...
[self downloadImages:arrayOfImages];
}
else
{
// Download is complete, use your images..
}
}
You can pass the number of downloads that failed, or even a delegate that will receive the images after.
You put a "note" and this may help, it allows you to display the images as they come down, I store the URLs in an array (here it is strings but you could do array of NSURL).
for(NSString *urlString in imageURLs)
{
// Add a downloading image to the array of images
[storedImages addObject:[UIImage imageNamed:#"downloading.png"]];
// Make a note of the image location in the array
__block int imageLocation = [storedImages count] - 1;
// Setup the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setTimeoutInterval: 10.0];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// Check the result
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (data != nil && error == nil && [httpResponse statusCode] == 200)
{
storedImages[imageLocation] = [UIImage imageWithData:data];
[self reloadImages];
}
else
{
// There was an error
recommendedThumbs[imageLocation] = [UIImageimageNamed:#"noimage.png"];
[self reloadImages]
}
}];
}
You then need another method which reloads the display. If the images are in a table then [[tableview] reloaddata];
-(void)reloadImages

Xcode, ensure codes after blocks run later for NSURLConnection asynchronous request

Hi there: I have been writing an iOS program which uses many http queries to the backend rails server, and hence there are tons of codes like below. In this case, it is updating a UITableView:
//making requests before this...
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse* response, NSData* data, NSError* error)
{
NSLog(#"Request sent!");
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Response code: %d", [httpResponse statusCode]);
if ([data length] > 0 && error == nil){
NSLog(#"%lu bytes of data was returned.", (unsigned long)[data length]); }
else if ([data length] == 0 &&
error == nil){
NSLog(#"No data was returned.");
}
else if (error != nil){
NSLog(#"Error happened = %#", error); }
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (jsonObject != nil && error == nil){
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]){
NSDictionary *deserializedDictionary = (NSDictionary *)jsonObject;
NSLog(#"Dersialized JSON Dictionary = %#", deserializedDictionary);
[listOfItems addObject:deserializedDictionary];
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Dersialized JSON Array = %#", deserializedArray);
[listOfItems addObjectsFromArray:deserializedArray];
}
else {
/* Some other object was returned. We don't know how to deal
with this situation as the deserializer only returns dictionaries
or arrays */ }
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data., Domain: %#, Code: %d", [error domain], [error code]);
}
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:YES];
}];
//the place where never runs
NSLog(#"End of function.");
Here is the problem: the last line gets executed usually before the code block. How do I ensure that the code after block actually runs after the block?
I am aware that the block uses some other threads, which is why I use performSelectorOnMainThread function instead of a direct call of [self.tableView reloadData]. But if I want to do something else afterward, how am I supposed to do?
Also, can anyone show some better ways to do this? I am trying to figure out the best way to make massive calls to the backend. There are several ways to make asynchronous requests, including this block way and another old-fashioned way invoking delegate classes. In the progress to refactor the codes, I also tried to create my own delegate class and let other classes invoke that, but it is difficult to identify the correct behaviour of callback functions for which connection's data it returns, especially for classes that use multiple functions to call different requests. And I don't want to use synchronous calls.
Thanks very much for any answers. Also welcome to point out any bugs in the code.
You can using dispatch group
Sample code:
- (void)doSomethingAndWait {
// synchronous method
// called in main thread is not good idea.
NSAssert(! [NSThread isMainThread], #"this method can't run in main thread.");
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
//making requests before this...
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse* response, NSData* data, NSError* error)
{
// your work here.
dispatch_group_leave(group);
}];
// wait for block finished
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
//will call until block is finished.
NSLog(#"End of function.");
}
And to call that method, you need avoid call it in main thread.
you should call it like this
dispatch_queue_t queue = dispatch_queue_create("com.COMPANYNAME.APPNAME.TASKNAME", NULL);
dispatch_async(queue, ^{
[self doSomethingAndWait];
});

Dictionary Getting Released?

Currently, I have a class that is parsing XML and sending the dictionary that the XML is parsed to to a view controller.
Here is a snippet of the code that sends the dictionary to the other class (where "response" is the dictionary):
if ([elementName isEqualToString:#"SessionData"])
{
// We reached the end of the XML document
// dumps dictionary into log
NSLog(#"Dump:%#", [response description]);
// sends dictionary to the VC
CardSetupViewController *setup = [[CardSetupViewController alloc]init];
setup.response = self.response;
//checks
NSLog(#"%# lololololol", [setup.response description]); //THIS WORKS FINE!!
return;
}
At that point, the code works fine. That NSLog marked with //THIS WORKS FINE!! works... obviously. Here is the method in the ViewController:
- (BOOL)authorize //this
{
AddCard *addCard = [[AddCard alloc]init];
ServerConnection *connection = [[ServerConnection alloc]init];
//XMLParser *xmlParser = [[XMLParser alloc]initXMLParser];
//serverReturn posts the data and is the ACTUAL server response in NSData form
NSData *serverReturn = [connection postData:[addCard textBoxToXml:
[self nameOnCardGet]:
[self ccNumGet]:
[self expMoGet]:
[self expYrGet]:
[self cvvGet]:
[self zipGet]:
[self nickNameGet]:
[self pinGet]]];
//This takes the information from the server and parses it to "response"
//Creates and inits NSXMLParser Object
NSXMLParser *nsXmlparser = [[NSXMLParser alloc] initWithData:serverReturn];
//Create and init our delegate
XMLParser *parser = [[XMLParser alloc] initXMLParser];
//set delegate
[nsXmlparser setDelegate:(id <NSXMLParserDelegate>) parser];
//initiates self.response THIS MAY NOT BE NEEDED
//response = [[NSMutableDictionary alloc]init];
//parsing
BOOL success = [nsXmlparser parse];
//error catch testing
if (success) {
NSLog(#"No errors");
}
else {
NSLog(#"Error parsing document!");
}
//dump
NSLog(#"ZOMG CHECK DIS OUT%#", [response description]);
return NO;
}
Basically, the NSLog that states "ZOMG CHECK DIS OUT" is returning (null) and I can't figure out why. No compilation errors, it is a property/synthesize as well. Any ideas?
Thanks in advance. Oh, and please excuse my NSLog comments. I had to differentiate from different parts of the code, and I was in a good mood.
Edit: I am using Automatic Reference Counting. Don't worry, nothing is leaking.
In your first code block, you generate a CardSetupViewController and then leak it. It is unrelated to whatever object is running the second code block. I assume that your second view controller is from your NIB?
Note that you're also leaking your NSXMLParser.
Your [response description], whatever that is, is probably an autoreleased object that gets released before ZOMG CHECK DIS OUT. Retain it and see if that works. Don't forget to release it when you're done with it.

NSOperationQueue and ASIHTTPRequest

I'm writing test cases for a wrapper class written around ASIHTTPRequest. For reasons I can't determine, my test cases complete with failure before the ASIHTTPRequest finishes.
Here's how the program flow works.
Start in my test case.
Init my http engine object, instruct it to create a new list
Create the new ASIHTTPRequest object and set it up.
Add the request to an operation queue.
Wait until that queue is empty
Check to see if my delegate methods were called and fail the test if they weren't.
Now, most of the time everything works fine and the test passes, but some of the time it fails because my delegate methods were called AFTER the operation queue returned control to my wait method.
Test Case
// Set my flags to 'NO'
- (void)setUp {
requestDidFinish = NO;
requestDidFail = NO;
}
- (void)testCreateList {
NSString *testList = #"{\"title\": \"This is a list\"}";
JKEngine *engine = [[JKEngine alloc] initWithDelegate:self];
NSString *requestIdentifier = [engine createList:jsonString];
[self waitUntilEngineDone:engine];
NSString *responseString = responseString_;
[engine release];
GHAssertNotNil(requestIdentifier, nil);
GHAssertTrue(requestDidFinish, nil);
GHAssertTrue([responseString hasPrefix:#"{\"CreateOrEditListResult\""], nil);
}
// Puts the test into a holding pattern until the http request is done
- (void)waitUntilEngineDone:(JKEngine *)engine {
[engine waitUntilFinishedRunning];
}
// The delegate method called on successful completion
- (void)requestFinished:(NSString *)requestIdentifier withResponse:(NSString *)response {
NSLog(#"request did finish");
requestDidFinish = YES;
responseIdentifier_ = [requestIdentifier retain];
responseString_ = [response retain];
}
Engine Code
- (NSString *)createList:(NSString *)list {
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request addRequestHeader:#"Content-Type" value:kContentType];
[request setRequestMethod:kPOST];
request.delegate = self;
[request appendPostData:[list dataUsingEncoding:NSUTF8StringEncoding]];
NSString *requestIdentifier = [NSString stringWithNewUUID];
[operationQueue_ addOperation:request];
[operationDictionary_ setObject:request forKey:requestIdentifier];
return requestIdentifier;
}
// This is the ASIHTTPRequest delegate method that's called on success
// but it sometimes isn't called until AFTER the operationQueue finishes running
- (void)requestFinished:(ASIHTTPRequest *)request {
DLog([request responseString]);
BOOL canNotifiyDelegate = [self.delegate respondsToSelector:#selector(requestFinished:withResponse:)];
if (canNotifiyDelegate) {
NSArray *keyArray = [operationDictionary_ allKeysForObject:request];
NSString *requestIdentifier = [keyArray objectAtIndex:0];
[operationDictionary_ removeObjectForKey:requestIdentifier];
if ([keyArray count] != 1) {
ALog(#"It looks like a request was added to the operation dictionary multiple times. There's a bug somewhere.", nil);
}
[self.delegate requestFinished:requestIdentifier withResponse:[request responseString]];
}
}
- (void)waitUntilFinishedRunning {
[operationQueue_ waitUntilAllOperationsAreFinished];
}
This is the way ASIHTTPRequest works. Delegate methods are called on the main thread, and calls to delegates do not block the request thread, so it's perfectly possible your delegates will be called after the queue finishes.
ASIHTTPRequest calls delegate methods on the main thread, by default GH-Unit runs its tests on a background thread. I'm still a little hazy on exactly what was going on, but forcing my network tests to run on the main thread fixed the problem.
I implemented the following method in my network test class.
- (BOOL)shouldRunOnMainThread {
return YES;
}