2

Is there a recommended way of implementing a series of activities such that the app remains responsive but the activities aren't all chained together in a nested manner and without using a thread.

Let me try and explain - suppose I have a model class which uses a downloader class to download a zip file, then when its downloaded the model unzips the file, then does something with the unzipped files.

Now if the downloader class informs the model class the download is ready via a delegate like this:

In the downloader class :

- (void) connectionDidFinishLoading:(NSURLConnection *)connection  {
    //  ...
    [self.delegate zipFileDownloadingCompleted];
}

In the model class :

- (void) zipFileDownloadingCompleted  {
    [self unzipFile];
    [self doSomethingElseWithFile];
    [self doSomeOtherStuff];
}

But if unZipFile and doSomethingElseWithFile and doSomeOtherStuff could be lengthy activities which use an asynchronous API then maybe the model class will split these up and only call doSomethingElseWithFile once a delegate on unzipFile has been called, and only call doSomeOtherStuff once a delegate has been called on doSomethingElseWithFile and so on. But is this a valid approach, seems to me everything is chained together and its still synchronous.

How could unZipFile and doSomethingElseWithFile and doSomeOtherStuff be broken up into separate chunks of activity? Is it possible for the app to make itself jump out of the run loop at the end of one activity and then jump back in to do the next activity?

If you're familiar with Brew then what I mean by this is perform one activity during one run through a HandleEvent() invocation, then self-schudule an event so that HandleEvent() will get called again to process the next activity and so on - so the app remains responsive at all time.

I would like to avoid threads if possible, I think event based coding is less complex and less error prone than threads.

I don't know if I explained that very well, hopefully you get the gist.

Gruntcakes
  • 37,738
  • 44
  • 184
  • 378

2 Answers2

1

One approach is to use performSelectorInBackground:withObject to execute long-running tasks asynchronously. You could replace your delegate's zipFileDownloadCompleted as follows:

- (void) zipFileDownloadCompleted {
  [self performSelectorInBackground:@selector(processAsync) withObject:nil];
}

- (void) processAsync  {
  if (!interrupt) {
    [self unzipFile];
    [self doSomethingElseWithFile];
    [self doSomeOtherStuff];
  }
}

Then in each long-running sub-task:

- (void) unzipFile {
  if (!interrupt) {
    ...
  }
}

I know you want to avoid threads, but this is about as easy as they get.

You may decide to put callbacks in those three methods to alert you to error conditions; you'd beef up processAsync with checks before initiating each step to fail cleanly, and so on. See the interrupt stuff in the edited examples (you'd use something more robust, manageable and safe than a global of course.)

Tim
  • 5,024
  • 2
  • 30
  • 58
  • Can the functionality executed by performSelectorInBackground be cancelled once its been started if its necessary to interrupt things before its finished executing? – Gruntcakes Dec 22 '11 at 21:05
  • @BiscutWoofington If you set it up to do so, then yes. Last paragraph of the answer hints at it, but basically if you set up some state checking and callbacks in `processAsync` and the three long-running methods, then you can cancel at any time. Have edited the answer a bit so you can see what I mean. – Tim Dec 22 '11 at 22:06
0

You might be looking for -[NSRunLoop performSelector:target:argument:order:modes:] (apple docs). I would check out this SO Q&A for more, specifically Brad Larson, he knows his stuff.

Community
  • 1
  • 1
Clay Bridges
  • 11,602
  • 10
  • 68
  • 118