Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone - a delay between operations in the queue

Tags:

iphone

I am adding operations to the queue using something like this

NSInvocationOperation *operation0 = [[NSInvocationOperation alloc] 
initWithTarget:self
selector:@selector(doStuff1) 
object:nil];

[queue addOperation:operation0];
[operation0 release];   


NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] 
initWithTarget:self
selector:@selector(doStuff2) 
object:nil];

[queue addOperation:operation1];
[operation1 release];   


NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] 
initWithTarget:self
selector:@selector(doStuff3) 
object:nil];

[queue addOperation:operation2];
[operation2 release];   

The queue is set to do just one operation at a time. So it will run the 3 methods without delay, one after another. Is that a way to add a small delay, lets say 0.5 seconds or whatever between each operation, so instead of running

doStuff1
doStuff2
doStuff3

the queue would do

doStuff1
sleep for X seconds
doStuff2
sleep for X seconds
doStuff3

?

thanks.

like image 431
Duck Avatar asked Dec 13 '10 11:12

Duck


4 Answers

Using sleep is a big waste of a thread. This is bad in the Ghostbusters' sense.

If you need a queue that executes its operations non-concurrently i.e. sequentially you can simpy set its maxConcurrentOperationCount variable to 1.

queue.maxConcurrentOperationCount = 1;

To pause between operations you could do two different things:

a) Delay the queueing of each operation:

//loop over the operations to be queued with and index
      double delayInSeconds = 2.0 * index;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            queue.addOperation(operation at index); 
        });

This will queue all operations without blocking.

b) Every operation has to implement

- (BOOL)isFinished

as you probably already know.

If your operation is "done" just delay setting its "done" variable by your desired delay. Use something like:

// Operation is done with its work, but we tell the Queue only after 2 seconds, so the queue will start the next operation later
double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
 __weak __typeof(self)weakSelf = self;
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[weakSelf willChangeValueForKey:@"isExecuting"];
    [weakSelf willChangeValueForKey:@"isFinished"];
    weakSelf.executing = NO;
    weakSelf.finished  = YES;
    [weakSelf didChangeValueForKey:@"isFinished"];
    [weakSelf didChangeValueForKey:@"isExecuting"];
    });

Variant a) guarantees a pause between starting times of operations. Variant b) guarantees a pause between the end of an operation and the start of a next operation.

like image 135
MacMark Avatar answered Sep 19 '22 18:09

MacMark


Without extra code, you can't really guarantee that your NSOperations will run in serial - if Apple release a multiprocessor iOS device, it will run multiple operations in parallel. So firstly, you should set up the dependency chain between your operations. I would suggest that you achieve this using NSOperation's - (void)addDependency:(NSOperation *)operation. You could also roll your own fancy locking code...

You can then add a sleep(5) to the end of each of your doStuff methods. If you need the sleep outside the doStuff methods, create a sleep NSOperation:

- (void)sleepOperationBody; {
sleep(0.5f);
}

NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] 
initWithTarget:self
selector:@selector(sleepOperationBody) 
object:nil];

That said, without knowing in more detail what you need to happen, I think you perhaps just need one NSOperation that combines all three tasks and puts a sleep between them. That would certainly be simpler code.

like image 30
Rog Avatar answered Sep 20 '22 18:09

Rog


I have two ways to do such thing.

First: Let the current thread to sleep on the tail of doStuff1, doStuff2, and doStuff3.

Second: Sub-class NSOperation and override the method -(void)main. You can init a custom NSOperation with parameters of target and action like following:

-(id)initWithTarget:(id)target action:(SEL)action;

And execute this action of target in the -(void)main by

[target performSelector:action];
[NSThread sleepForTimeInterval:0.05];
like image 41
AechoLiu Avatar answered Sep 16 '22 18:09

AechoLiu


you can do like following manner

[operation0 addDependancy:operation1];
[operation1 addDependancy:operation2];

and now add [NSThread sleepforTimeInterval:5] to at the end of each task

like image 30
night bird Avatar answered Sep 20 '22 18:09

night bird