How refresh UIVIewController to NSOperationQueue - iphone

I have a view where I show an UIImageView which complimentary internet, I use to bring data NSXMLparser which are loaded correctly, the problem was that I use to make the parser NSOperationQueue background so then I refresh the image in my main view. image which form no refresh them in any way
I leave here the code below
- (void)viewDidLoad
{
[Base64 initialize];
[super viewDidLoad];
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(loadDataWithOperation)
object:nil];
[queue addOperation:operation];
[operation release];
}
- (void) loadDataWithOperation {
getData=NO;
NSURL *url1 = [ NSURL URLWithString:[url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *xmlData = [NSData dataWithContentsOfURL:url1];
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[self performSelectorOnMainThread:#selector(showImage)withObject:nil waitUntilDone:NO];
}
-(void)showImage
{
NSArray *imagen =[[NSArray alloc] initWithArray:xml];
NSString *hola1 =[[imagen objectAtIndex:5]objectForKey:#"imagen"];
NSData * dataa = [Base64 decode:hola1];
img = [UIImage imageWithData:dataa];
self.images.image = img;
[images setImage:img];
[img release];
}
What am I doing wrong? appreciate your help please

I don't see anything obvious (though obviously we don't see the building of the xml array nor do we know precisely what the XML itself looks like). You should NSLog (or set a breakpoint and manually inspect) the xml as well as hola1 and dataa results in showImage, to identify at precisely what's going on at each step.
There are two possible problems with background operations and downloading XML. Neither of these seem applicable here, but I'll mention it just in case:
But there's nothing that would prevent the downloading and parsing of your XML like you have demonstrated here. If you were using NSURLConnection to download the XML (perhaps you simplified it here for the purposes of demonstration), there are issues in using NSURLConnection in a background queue. But if you use dataWithContentsOfURL (or better, NSXMLParser method initWithContentsOfURL) that wouldn't be an issue.
It looks like you're downloading a single XML, but if you were downloading multiple XML sources simultaneously, you should appreciate that many servers impose a limit as to how many concurrent connections they'll allow from a single client. You can use NSOperationQueue property maxConcurrentOperationCount to mitigate that problem. Four is a typical value.
Unrelated, but there are a couple of minor memory management things you might want to look at:
if you already have xml you don't need to create a new imagen;
you should probably release the queue object after adding the operation, or if you need to keep it around to reuse it, you should make it a class property or instance variable;
you could get rid of operation completely if you did:
[queue addOperationWithBlock:^{
[self loadDataWithOperation];
}];
you should release the parser object when you're done with it;
if you keep imagen in your code you should also release it when done; and
you should not release the img object since imageWithData returns an autorelease object
The routine memory management stuff probably would be pointed out to you if you did a static analysis (choose "Analyze" from the "Product" menu).
One final observation:
I notice that you have:
NSData *xmlData = [NSData dataWithContentsOfURL:url1];
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
Clearly that xmlData is not necessary because you could have also just have done:
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url1];

Related

Need to use NSOperationQueue to parse two different NSOperation class

Trying to parse two different URLs which has XML data.
static NSString *string1 = #"http://abc.com/abc1.xml";
NSURLRequest *URL1 =[NSURLRequest requestWithURL:[NSURL URLWithString:string1]];
self.URL1Connection =[[[NSURLConnection alloc] initWithRequest:URL1 delegate:self] autorelease];
static NSString *string2 = #"http://abc.com/abc2.xml";
NSURLRequest *URL2 =[NSURLRequest requestWithURL:[NSURL URLWithString:string2]];
self.URL2Connection =[[[NSURLConnection alloc] initWithRequest:URL2 delegate:self] autorelease];
I have two different NSOperation class both working independently as both have their own work to finish.
I have a parseQueue which is NSOperationqueue in which I have added two different operations.
TestOperation *testOperation = [[TestOperation alloc]
initWithData:self.data1 delegate:self ];
[self.parseQueue addOperation:testOperation];
[testOperation release]; // once added to the NSOperationQueue it's retained, we don't need it anymore
testOperation = nil;
Test1Operation *test1Operation = [[Test1Operation alloc]
initWithData:self.data2];
[self.parseQueue addOperation:test1Operation];
[test1Operation release]; // once added to the NSOperationQueue it's retained, we don't need it anymore
test1Operation = nil;
Basically I am trying to parse the two xml data separately and want to have concurrent operations. But when the second operation gets over adding in the queue, it still looks at the first class operation. I am lost in this since I have no idea why it is still looking for the first class even after release. Can anybody throw some ideas and help me.
I figured out the answer.
Need to call each XML URL in their own respective class and call NSOperation for each call seperately. Instead of calling on application delegate method, call on viewdidload or viewdidappear method as required.
Once finished parsing, notify the main thread that the parsing is over and return the result.
Sagos

NSAutoreleasePool issues on the iPhone

I have a small iPhone app which has a button on the first view. When I select this button I load up my new view which has an image on it. I'm currently using the following code to load the image from an online source on a separate thread, whilst allowing the user to continue controlling the application:
- (void) loadImageTest
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [[NSURL alloc] init];
url = [NSURL URLWithString:logoPath];
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
//[pool drain];
[pool release];
}
This is called from this line of code in the new view's init:
[NSThread detachNewThreadSelector:#selector(loadImageTest) toTarget:self withObject:nil];
Now this works fine (ish), but if I close the new view and then load a new one up again in quick succession (or just after-wards in some cases) it will bomb out with the classic EXC_BAD_ACCESS. I'm sure that this is due to the code within the thread pool, but can anyone see why this is happening?
Thanks
Yours:
// This is ok, you might try using NSURLConnections asynchronous methods instead of making
// your own thread.
[NSThread detachNewThreadSelector:#selector(loadImageTest) toTarget:self withObject:nil];
- (void)loadImageTest
{
// This is fine
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// you're making and then abandoning this url object so it will leak
NSURL *url = [[NSURL alloc] init];
// this is fine
url = [NSURL URLWithString:logoPath];
// again making and abandoning an object
NSData *data = [[NSData alloc] init];
data = [NSData dataWithContentsOfURL:url];
// this works, but is not thread safe,
// can't operate on UIKit objects off the
// main thread
loadingImage = [UIImage imageWithData:data];
titleLogoImage.image = loadingImage;
// this is fine
//[pool drain];
[pool release];
}
Cleaned up to make things thread safe, etc. Should help:
// I'm assuming you have a iVar for logoPath but we don't want to
// make threaded calls to an iVar (it's not mutable, so you could do it, but it's just bad form)
// If i'm wrong about logoPath being an iVar don't sweat copying it.
- (void)whateverMethodYouWant
{
NSString *aLogoPath = [[logoPath copy] autorelease];
[NSThread detachNewThreadSelector:#selector(loadImageForPath:) toTarget:self withObject:aLogoPath];
}
- (void)loadImageForPath:(NSString *)aLogoPath
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:aLogoPath]];
// the performSelector will retain the data until the method can be performed
[self performSelectorOnMainThread:#selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
[pool release];
}
- (void)setImageForTitleLogo:(NSData *)imgData
{
// This part is not strictly necessary
// but it's a nice way to wrap a method that needs to happen on the main thread.
if ([NSThread isMainThread])
{
// make the image (i'm assuming you meant loadingImage to be a local scope variable)
UIImage *loadingImage = [UIImage imageWithData:imgData];
// make sure the button still exists
// also make sure you're setting any references to this button to nil when you're releasing and making new views
// so that you're not addressing a button that's been released
// (assigning the image to the button will cause the button to retain it for you)
if (titleLogoImage != nil)
titleLogoImage.image = loadingImage;
}
else
{
[self performSelectorOnMainThread:#selector(setImageForTitleLogo:) withObject:imgData waitUntilDone:NO];
}
}
You're doing weird stuff.
NSURL *url = [[NSURL alloc] init];
means you create an NSURL object, which you own.
url = [NSURL URLWithString:logoPath];
This means you create another NSURL object, which is autoreleased. The NSURL you just created, just leaks. The same goes for the NSData here.
The loadingImage must be retained by titleLogoImage, or it will be deallocated on the drain of your NSAutoReleasePool. What is titleLogoImage and does it retain image?
edit ps: what also disturbes me, is that loadingImage is not confined to the scope of the function. To cut things short:
NSURL *url = [NSURL URLWithString:logoPath];
NSData *data = [NSData dataWithContentsOfURL:url];
titleLogoImage.image = [UIImage imageWithData:data];
may save some headaches, at least.
There is nothing in the posted code that would trigger a crash. Depending on how titleLogoImage is defined, it might be affects by the autorelease.
However, beyond the problems outlined by mvds, you have no pressing needs for a localized autorelease pool. It will do nothing here but cause you trouble.
Autorelease pools are dangerous and not for beginners. They will kill any autoreleased objects in them. You generally only create your own pool when you are rapidly creating a large number of memory intensive objects. That does not appear to be the case here.
Despite the attention given them, custom pools are seldom used. After over a decade Apple API work, I can probably count on my fingers the number of times I have used my own custom pool.

Stop NSXMLParser Instance from Causing _NSAutoreleaseNoPool

In my iPhone application, I have an instance of NSXMLParser that is set to a custom delegate to read the XML. This is then moved into its own thread so it can update the data in the background. However, ever since I have done this, it has been giving me a lot of _NSAutoreleaseNoPool warnings in the console. I have tried to add a NSAutoreleasePool to each of my delegate classes, however, this hasn't seemed to solve the problem. I have included my method of creating the NSXMLParser in case that is at fault.
NSURL *url = [[NSURL alloc] initWithString:#"http://www.mywebsite.com/xmlsource.xml"];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
CustomXMLParser *parser = [[CustomXMLParser alloc] init];
parser.managedObjectContext = self.managedObjectContext;
parser = [parser initXMLParser];
[xmlParser setDelegate:parser];
[NSThread detachNewThreadSelector:#selector(parse) toTarget:xmlParser withObject:nil];
If anyone has any ideas to get rid of this problem, I would really appreciate it.
Thanks.
In objective-c each thread needs its own NSAutorelease pool to handle autoreleased objects. So in your parse method you need to create (and release) NSAutoreleasePool object:
- (void)parse{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
...
// your method implementation
...
[pool release];
}

iPhone: Parsing multiple XML with NSXMLParser in background disturbing each other

I have a strange issue, when it comes to parsing XML with NSXMLParser on the iPhone. When starting the app, I want to preload 4 table-views, that are populated by RSS-Feeds in the background.
When I init the table-views one-by-one, than loading, parsing and displaying all works like a charm. But when I try to init all view at once (at the same time), than it seems, that the XML-parser-instances are disturbing each other. Somehow data from one XML-Feed are "broadcasted" into other xml-parser instances, where they do not belong. Example: there is a "teammember" item, with "This is my name". When this bug occurs, there is a string from another xml-feed added, i.e. resulting in: "This is my name58", where 58 is the chart-position of something from the other view. "58" seems to miss then on the other instance.
It looks to me, that this bug occurs because of the NSXMLParser-delegate method:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentStringValue) {
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
In this case "by coincidence" bytes are appended to strings, where they do not belong to.
The strange thing is, that every instance of NSXMLParser is unique, got its own unique delegates, that are attached to their own ViewController. Every parsing-requests spawns it own background-task, with its own (also also unique named) Autorelease-pool.
I am calling the NSXMLParser like this in the ViewController:
// prepare XML saving and parsing
currentStringValue = [[[NSMutableString alloc] initWithCapacity:50] retain];
charts = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://(SOME XML URL)"];
xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] retain];
//Set delegate
[xmlParser setDelegate:self];
//loading indicator
progressWheel = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(150.0,170.0,20.0,20.0)] autorelease];
progressWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.view addSubview:progressWheel];
[progressWheel startAnimating];
// start loading and parsing the xml-feed in the background
//[self performSelectorInBackground:#selector(parse:) withObject:xmlParser]; -> I also tried this
[NSThread detachNewThreadSelector:#selector(parse:) toTarget:self withObject:xmlParser];
And this is one of the background-tasks, parsing the feed:
-(void)parse:(NSXMLParser*)myParser {
NSAutoreleasePool *schedulePool = [[NSAutoreleasePool alloc] init];
BOOL success = [myParser parse];
if(success) {
NSLog(#"No Errors. xmlParser got: %#", myParser);
(POST-PROCESSING DETAILS OF THE DATA RETURNED)
[self.tableView reloadData];
} else {
NSLog(#"Couldn't initalize XMLparser");
}
[progressWheel stopAnimating];
[schedulePool drain];
[myParser release];
}
What could cause this issue? Am I calling the background-task in the right way? Why is this bug approaching, since every XML-Parser got its own, unique instance?
You should not be updating UI elements (like progressWheel) from inside a background thread. UI updates should be done on the main thread.
Use -performSelectorOnMainThread:withObject:waitUntilDone: to update UI elements from within a background thread.
I've released an open source RSS/Atom Parser for iPhone and it makes reading and parsing web feeds extremely easy.
You can set it to download the data asynchronously, or you could run it in a background thread synchronously to collect the feed data.
Hope this helps!

NSThread crashes on second call (iPhone)

I have an object and in that object I start my thread (for loading doing some URL loading).
When I have a return of my data I call a selector to perform on the main thread.
Works fine if I call it the first time, but the second time it crashes (no specific error).
[NSThread detachNewThreadSelector:#selector(doThread:)
toTarget:self
withObject:#"lala"];
-(void) doThread:(NSString *)poststring {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
DataModelLocator *mydelegate = [DataModelLocator instance];
NSData *postData = [poststring dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
NSURL *url = [NSURL URLWithString:[mydelegate fwaservicepath]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPBody:postData];
NSURLResponse *urlResponse;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:nil];
if(data) {
[self performSelectorOnMainThread:#selector(loadDidFinishWithData:)
withObject:data
waitUntilDone:YES];
//[self loadDidFinishWithData:data];
} else {
[self performSelectorOnMainThread:#selector(loadDidFinishWithError:)
withObject:data
waitUntilDone:YES];
}
[pool release];
}
}
It crashes when I call performSelectorOnMaintThread... Could it be that it crashes on a singleton, when it got released?
When dealing with threads, avoid autoreleased objects like the plague. The autorelease pools will be drained at nondeterministic times, causing fun crashes. Use alloc/init and release on all objects involved, making sure to retain all objects that you take in on methods that are called from another thread using performSelectorOnMainThread or detachNewThreadSelector.
Garbage collection on the Mac effectively solves these problems, but the iPhone is not going to have that any time soon.
You may want to post some more information about your problem (which of the two lines crashes, what you've figured out from debugging so far, etc.) so that we can offer you some better suggestions. Without knowing which line is giving you problems, I'll hazard a guess: from the sound of it, you might have an object somewhere that's getting cleaned up by the automatic garbage collection.
Where is the variable "data" coming from? If you're creating it in the header file as a private member variable, you might have something like:
NSSomeType *data = [NSSomeType builtInInitFunction];
A variable initialized like this will normally be autoreleased, but you probably want to make sure that the garbage collection retains the instance of that object. Try something like:
// Objects initialized with init are retained
NSSomeType *data = [[NSSomeType alloc] init];
// Objects that would normally be autoreleased can be marked as retain
NSSomeType *data = [[NSSomeType builtInInitFunction] retain];
I'm not sure how your code is structured, but be sure to add at least one release for every retain and init! I'm still pretty new to Objective-C, so it's a little bit like the blind leading the blind so take my advice with a grain of salt.
Check out the "More on Memory Management" section of Learn Objective-C for more info.
EDIT2: Clarified example code. Thanks to Evan (comments) for the help.
EDIT3: I agree with Brad. Consider removing the AutoRelease pool you have an handling your alloc/init/release yourself. I don't know enough about the NSURLConnection object to know this, but is your *data memory being marked as Autorelease? If so, you may need to initialize in a different way or use retain.
Step through your code in the debugger. Figure out A) exactly which line is crashing and then B) the values of all of your variables. If you're lucky, you'll notice one is nil.