6

Working with some code, I'm coming across run loops, which I'm new to, inside NSOperations.

The NSOperations are busy downloading data - and whilst they are busy, there is code to wait for the downloads to complete, in the form of NSRunLoops and thread sleeping.

This code in particular is of interest to me:

while (aCertainConditionIsTrue && [self isCancelled]==NO) {
     if(![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]){
        [NSThread sleepForTimeInterval:1.0];
     }
}

I've read about the run loops, and runMode:beforeDate: will wait for an input source or a timeout. Although I'm not 100% what counts as an input souce.

On the first execution of this it always returns NO and hits the sleepForTimeInterval:. Is this bad?

In a particular utility class, it's hitting the sleepForTimeInterval: a lot - once for each thread - which significantly hurts the performance.

Any better solutions for this, or advice?

Costique
  • 23,712
  • 4
  • 76
  • 79
bandejapaisa
  • 26,576
  • 13
  • 94
  • 112

2 Answers2

2

Sleeping locks up the thread. Perhaps you change your code to use performSelector:withObject:afterDelay. That way your thread can continue to run.

    ...
    done = NO;
    [self checkDoneCondition:nil];
    ...

- (void)checkDoneCondition:(id)object {
    if (aCertainConditionIsTrue && [self isCancelled]==NO) {
        if(...) {
            [self performSelector:@selector(checkDoneCondition:) withObject:[con error] afterDelay:1.0];
        } else {
            done = YES;
        }
    }
}
jimmyg
  • 580
  • 4
  • 13
1

It looks like you need to use a concurrent NSOperation. Here is the relevant part in the Apple docs:

In contrast to a non-concurrent operation, which runs synchronously, a concurrent operation runs asynchronously. In other words, when you call the start method of a concurrent operation, that method could return before the corresponding task is completed. This might happen because the operation object created a new thread to execute the task or because the operation called an asynchronous function. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing. (...) In a concurrent operation, your start method is responsible for starting the operation in an asynchronous manner. Whether you spawn a thread or call an asynchronous function, you do it from this method. Upon starting the operation, your start method should also update the execution state of the operation as reported by the isExecuting method. You do this by sending out KVO notifications for the isExecuting key path, which lets interested clients know that the operation is now running. Your isExecuting method must also return the status in a thread-safe manner.

(from https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html)

In other words, you can override the -start method in your NSOperation subclass, and have ivar for the executing and finished property. This method will start the download in a separate thread. When the download starts, you set the executing flag and trigger KVO. WHen it is finished in this thread, you do the same with finished and executing. It seems complicated but it's actually quite simple.

See also this question on Stack Overflow with a great explanation: Subclassing NSOperation to be concurrent and cancellable

Community
  • 1
  • 1
charles
  • 11,212
  • 3
  • 31
  • 46