Question

I have a Download object which handles NSURLConnection.

Then I have NSOperation object (DownloadOperation) which holds Download object as property.

Download object has ability to start/pause/resume/cancel.

This is the main method of DownloadOperation

- (void)main
{
    @autoreleasepool {

        BOOL isDone = NO;

        if (![self isCancelled]) {   
            [_download start]; //Download object start (creates NSURLConnection internally)
        }

        NSDate *distantFuture = [NSDate distantFuture];
        while(!isDone && ![self isCancelled]) {

            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:distantFuture];

            if (!_download.isActive) { //internal state of Download
                isDone = YES;
            }
        }

        [self completeOperation]; //this sets finished and executing flags
    }
}

From outside (from UI), I manipulate with Download object: Start, Pause, Resume, Cancel. And internally I am changing its state so when Download is finished or canceled, isActive is set to NO and while loop should end.

This works if I start Download and let it finish (in background, NSURLConnection finished and called delegate -connectionDidFinish...)

If I pause/resume Download, download will continue to download and finish (and change its internal state: isActive -> NO). On pause I cancel NSURLConnection and on resume I create new.

Or if I cancel the download, it will also be inactive (NSURLConnection is canceled).

But here is the problem:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:distantFuture]; never returns in those cases (when I cancel NSURLConnection) and my "if" statement is never handled so this DownloadOperation is always considered running and will never exit my NSOperationQueue.

It looks like there is no event that could be fired that would cause that runloop to wake up.

I tried

            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];

and this kind of works but not so smooth and I don't think it is the best solution.

What I really want to know is, how to force that NSRunLoop to wake up (how to fire some event that will cause it to wake up) and continue my while loop?

Was it helpful?

Solution

Here's what I did.

Good thing is that I have notifications in my app and I know exactly when downloads are changing. So I've made an instance variable NSThread currentThread and at the beginning of main I call currentThread = [NSThread currentThread].

After I receive notification which I know should cause my thread to wake up I call:

[self performSelector:@selector(wakeUpThread:) onThread:currentThread withObject:nil waitUntilDone:NO];

- (void)wakeUpThread does nothing, it is empty method, but purpose of this is to wake up thread and cause my run loop to continue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top