I'm trying to get data from a website- xml. Everything works fine.
But the UIButton remains pressed until the xml data is returned and thus if theres a problem with the internet service, it cant be corrected and the app is virtually unusable.
here are the calls:
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.XMLdataArray.count > 0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray.
}
XMLViewController *controller = [[XMLViewController alloc] initWithNibName:@"MedGearsApps" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
It works fine, but how can I make the view buttons functional with getting stuck. In other words, I just want the UIButton and other UIButtons to be functional whiles the thing works in the background.
I heard about performSelectorInMainThread but i cant put it to practice correctly
any help is appreciated :)
You don’t understand the threading model much and you’re probably going to shoot yourself in the foot if you start adding asynchronous code without really understanding what’s going on.
The code you wrote runs in the main application thread. But when you think about it, you don’t have to write no
main
function — you just implement the application delegate and the event callbacks (such as touch handlers) and somehow they run automatically when the time comes. This is not a magic, this is simply a Cocoa object called a Run Loop.Run Loop is an object that receives all events, processes timers (as in
NSTimer
) and runs your code. Which means that when you, for example, do something when the user taps a button, the call tree looks a bit like this:Now
yourCode
does what you want to do and the Run Loop continues running. But when your code takes too long to finish, such as in your case, the Run Loop has to wait and therefore the events will not get processed until your code finishes. This is what you see in your application.To solve the situation you have to run the long operation in another thread. This is not very hard, but you’ll have to think of a few potential problems nevertheless. Running in another thread can be as easy as calling
performSelectorInBackground
:And now you have to think of a way to tell the application the data has been loaded, such as using a notification or calling a selector on the main thread. By the way: storing the data in the application delegate (or even using the application delegate for loading the data) is not very elegant solution, but that’s another story.
If you do choose the
performSelectorInBackground
solution, take a look at a related question about memory management in secondary threads. You’ll need your own autorelease pool so that you won’t leak autoreleased objects.Updating the answer after some time – nowadays it’s usually best to run the code in background using Grand Central Dispatch:
You can make use of a background operation that gets pushed into the operation queue:
I've created specific "commands" that get executed in the background:
@end
After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.
Use NSURLConnection's connectionWithRequest:delegate: method. This will cause the specified request to be sent asynchronously. The delegate should respond to connection:didReceiveResponse: and will be sent that message once the response is completely received.
It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.