views:

720

answers:

4

Hi guys,

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 :)

A: 

You can make use of a background operation that gets pushed into the operation queue:

BGOperation *op = [[BGOperation alloc] init];
[[self operationQueue] addOperation:op];
[op release];

I've created specific "commands" that get executed in the background:

@implementation BGOperation

# pragma mark Memory Management

- (BGOperation *)init
{
if ((self = [super init]) != nil)
    /* nothing */;
return self;
}

- (void)dealloc
{
self.jobId = nil;
[super dealloc];
}

# pragma mark -
# pragma mark Background Operation

- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [appDelegate GetApps];
[pool release];
return;
}

@end

After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.

Ralf Edmund
Isn’t it much easier to use `performSelectorInBackground` or the asynchronous `NSURLConnection` mode?
zoul
No, because then you have to handle the thread lifecycle (in the given example he did not need to create his own autorelease pool). The Asynch NSUrlConnection is OK but it works on the main thread and so can make your UI stutter, especially if you have a few of them going at once. OperationQueues are far, far easier to work with and manage...
Kendall Helmstetter Gelner
Creating and draining an autorelease pool is two lines of code, it does not seem like a big deal to me. (Just a point of view.)
zoul
On my part, i prefer the `NSOperation` based solution. My applications typically do have cached database containing the remote information and using background processes i synchronize the state of the servers database with this distributed copy.This way the application is really "snappy" while still keeping up to date in with the most recent information in the back. I can't do this using backgrounded `NSURLConnection` instances only.
Ralf Edmund
A: 

It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.

Paul Lynch
+1  A: 

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.

JeremyP
A: 

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:

main thread running
    main run loop
        // fire timers
        // receive events — aha, here we have an event, let’s call the handler
        view::touchesBegan…
            // use tapped some button, let’s fire the callback
            someButton::touchUpInside
                yourCode

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:

[appDelegate performSelectorInBackground:@selector(GetApps) withObject:nil];

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.)

zoul
Yea, i did read up on these things, theres still alot more to understand, but I'll get it, you've put me in the right direction. thanks. I was using TBXML component, so, i guess i need to write up a simple one to understand how it works. anyway, Another related question was, if the user chooses to close the view, is there anyway to terminate the selectorInBackground call? on ViewDidUnload ..?
Sam
That depends. In simple cases you can just ignore the issue and code the notification part so that it does not break when the view gets dismissed early. A more bullet-proof solution can be written using the asynchronous `NSURLConnection` mode or the operation queue, both have a method to cancel the operation in progress.
zoul