views:

386

answers:

3

Hi

I have some web service data in my app that needs to be updated every 3 minutes. I had tried out a few approaches but got a really good piece of advise in here last week, I should not build a new thread every 3 minutes and then subsequently try and dealloc and synchronize all the different parts so that I avoided memory bug. Instead I should have a "worker thread" that was always running, but only did actual work when I asked it too (every 3 minutes).

As my small POC works now, I spawn a new thread in the applicationDidFinishLaunching method. I do this like so:

[NSThread detachNewThreadSelector:@selector(updateModel) toTarget:self withObject:nil];

- (void) updateModel {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    BackgroundUpdate *update = [[BackgroundUpdate alloc] initWithTimerInterval:180];
    [update release];
    [pool release];
}

Ok, this inits the "BackgroundUpdate" object with the update interval in seconds. Inside the updater it is simply like this for now:

@implementation BackgroundUpdate

- (id) initWithTimerInterval:(NSInteger) secondsBetweenUpdates {

    if(self = [super init]) {

        [NSTimer scheduledTimerWithTimeInterval:secondsBetweenUpdates 
                                        target:self 
                                        selector:@selector(testIfUpdateNeeded) 
                                        userInfo:nil 
                                        repeats:YES];
    }

    return self;
}

- (void) testIfUpdateNeeded {

    NSLog(@"Im contemplating an update...");

}

I have never used threads like this before. I has always been "setup autoReleasePool, do work, get your autoReleasePool drained, and goodbye".

My problem is that as soon as the initWithTimerInterval has run, the NSThread is done, it therefore returns to the updateModel method and has its pool drained. I guess it has to do with the NSTimer having it's own thread/runloop? I would like for the thread to keep having the testIfUpdateNeeded method run every 3 minutes.

So how will I keep this NSThread alive for the entire duration of my app?

Thank You for any help/advice given:)

A: 

Hi, you don't need to create thread if you want run your NSTimer. The NSTimer is already running in a separate run loop, you have to just declare your NSTimer like in your initWithTimerInterval in the applicationDidFinishLaunching :

- (void)applicationDidFinishLaunching:(UIApplication *)application {

    yourTimer = [NSTimer scheduledTimerWithTimeInterval:300 
                                        target:self 
                                        selector:@selector(testIfUpdateNeeded) 
                                        userInfo:nil 
                                        repeats:YES];
    //...
}

- (void) testIfUpdateNeeded {
    NSLog(@"Im contemplating an update...");
}

As you are in the UIApplicationDelegate you'll never release this object. When you quit your application think to invalidate the timer like that:

- (void)applicationWillTerminate:(UIApplication *)application {
   [yourTimer invalidate];
}

Otherwise to keep a thread alive just use an infinite loop as while(YES);

Yannick L.
Hi Yannick.Thanks, I must have been unclear on the update part:) Where it says:NSLog(@"Im contemplating an update..."); I would put my web service code and call a bunch of methods. This should be in a different thread. I would just like to have the timer fire every 3 minutes and call testIfUpdateNeeded but besides from that it should do as little as possible. I thought about a while(true) loop and a sleep(3 * 60 * 1000) first, then an update block. But is that not considered really bad practice? It was in my C days anyways:)
RickiG
In the testIfUpdateNeeded you can create a thread to execute your bunch of methods. The NSTimer will call this method every 3 mins and at each time it'll create a new thread which will be execute before to died.
Yannick L.
That is what I already did:/ This lead to a mirage of timing and strong coupling trouble in my setup. There is no need to tear everything down and build it up again every 3 minutes. If I add a BackgroundUpdate object to the appDelegate and this object is not run in a separate thread, but instead contains a separate thread. My appDelegate would wait on the BackgroundUpdate to give back the run loop.In my appDelegate I would like to spawn this thread that deals with data and let my main thread deal with the UI only. Is it possible to keep the NSThread as a property of the appDelegate?Thanks
RickiG
I am not trying to shoot solutions down:) I have just seen many references to this "worker thread" practice and many warnings about using NSThreadSleepForInterval, sleep while(true)etc. They all say "use an NSTimer and a thread that lives all through the app". I just can't find out exactly how. Maybe I am explaining it in a confusing way or maybe I misunderstood something:/
RickiG
Yes you can keep the NSThread as a property of the appDelegate using the "- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument" method and then use the start method on the instance of the thread object.Yes it's better to use only one thread because the creation of a thread costs a lot of resources. However if you want to sleep a thread there is not a lot of solutions. :/
Yannick L.
A: 

It sounds like you might want an NSOperation instead of an old fashion thread. You could active the operation via a universal timer then it would execute on its own thread and then clean up its own memory when done.

TechZen
Thanks TechZen. I read parts of the guide you linked to and learned something new:) They however don't recommend NSOperation for repetitive tasks, but rather for queuing an arbitrary set of tasks that has dependencies. Why it is not just the "Thread in a box" solution, however, I cant say. It did sound very promising at first:)
RickiG
Okay, I missed that. Good to know.
TechZen
+2  A: 

You're close. All you need to do now is start the run loop running so the thread doesn't exit and the timer runs. After your call to initWithTimerInterval:, just call

[[NSRunLoop currentRunLoop] run];

The thread will run its run loop indefinitely and your timer will work.

Ken Aspeslagh
Thank You Ken, exactly:)It now ticks along and when the timer fires I get my model updated:)
RickiG