dispatch_after equivalent in NSOperationQueue
Asked Answered
F

7

18

I'm moving my code from regular GCD to NSOperationQueue because I need some of the functionality. A lot of my code relies on dispatch_after in order to work properly. Is there a way to do something similar with an NSOperation?

This is some of my code that needs to be converted to NSOperation. If you could provide an example of converting it using this code, that would be great.

dispatch_queue_t queue = dispatch_queue_create("com.cue.MainFade", NULL);
dispatch_time_t mainPopTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeRun * NSEC_PER_SEC));
dispatch_after(mainPopTime, queue, ^(void){
    if(dFade !=nil){
        double incriment = ([dFade volume] / [self fadeOut])/10; //incriment per .1 seconds.
        [self doDelayFadeOut:incriment with:dFade on:dispatch_queue_create("com.cue.MainFade", 0)];
    }

});
Flush answered 18/3, 2013 at 15:31 Comment(0)
R
30

NSOperationQueue doesn't have any timing mechanism in it. If you need to set up a delay like this and then execute an operation, you'll want to schedule the NSOperation from the dispatch_after in order to handle both the delay and making the final code an NSOperation.

NSOperation is designed to handle more-or-less batch operations. The use case is slightly different from GCD, and in fact uses GCD on platforms with GCD.

If the problem you are trying to solve is to get a cancelable timer notification, I'd suggest using NSTimer and invalidating it if you need to cancel it. Then, in response to the timer, you can execute your code, or use a dispatch queue or NSOperationQueue.

Reseat answered 18/3, 2013 at 15:39 Comment(1)
Using timer vs timer notification use more resources and battery, and need some extra work to continue on background. But it's easier to cancel yes.Optometry
T
2

You can keep using dispatch_after() with a global queue, then schedule the operation on your operation queue. Blocks passed to dispatch_after() don't execute after the specified time, they are simply scheduled after that time.

Something like:

dispatch_after
(
    mainPopTime,
    dispatch_get_main_queue(),
    ^ {
        [myOperationQueue addOperation:theOperationObject];
    }
);
Thirza answered 18/3, 2013 at 15:36 Comment(0)
S
1

Seven years late, but iOS 7 introduced this functionality to OperationQueue.

https://developer.apple.com/documentation/foundation/operationqueue/3329365-schedule

Saltire answered 12/8, 2020 at 22:35 Comment(0)
P
0

You could make an NSOperation that performs a sleep: MYDelayOperation. Then add it as a dependency for your real work operation.

@interface MYDelayOperation : NSOperation
...
- (void)main
{
    [NSThread sleepForTimeInterval:delay]; // delay is passed in constructor
}

Usage:

NSOperation *theOperationObject = ...
MYDelayOperation *delayOp = [[MYDelayOperation alloc] initWithDelay:5];
[theOperationObject addDependency:delayOp];
[myOperationQueue addOperations:@[ delayOp, theOperationObject] waitUntilFinished:NO];
Pubilis answered 11/4, 2014 at 6:55 Comment(3)
This will keep the operation active in the queue, and in case of a queue with a limited number of active operation, that sleep will hold other eligible operations from executing.Shallop
Agreed with @Bogdan. This is not a good approach. You should never block the operation queue threads.Harsh
Like this one! Comparing to approaches proposed in other answers this one is the cleanest.Amiss
S
0
[operationQueue performSelector:@selector(addOperation:) 
                     withObject:operation 
                     afterDelay:delay];
Shallop answered 24/10, 2016 at 12:55 Comment(0)
O
0

Swift 4:

DispatchQueue.global().asyncAfter(deadline: .now() + 10 { [weak self] in
                guard let `self` = self else {return}

                self. myOperationQueue.addOperation {
                    //...code...
                }
            }
Optometry answered 7/2, 2019 at 9:49 Comment(0)
I
0

I used following code to get delayed call of operation:

class DelayedBlockOperation: Operation {
    private let deadline: DispatchTime
    private let block: (() -> Void)?
    private let queue: DispatchQueue

    override var isAsynchronous: Bool { true }
    override var isExecuting: Bool {
        get { _executing }
        set {
            willChangeValue(forKey: "isExecuting")
            _executing = newValue
            didChangeValue(forKey: "isExecuting")
        }
    }
    private var _executing: Bool = false

    override var isFinished: Bool {
        get { _finished }
        set {
            willChangeValue(forKey: "isFinished")
            _finished = newValue
            didChangeValue(forKey: "isFinished")
        }
    }
    private var _finished: Bool = false

    init(deadline: DispatchTime,
         queue: DispatchQueue = .global(),
         _ block: @escaping () -> Void = { }) {
        self.deadline = deadline
        self.queue = queue
        self.block = block
    }

    override func start() {
        queue.asyncAfter(deadline: deadline) {
            guard !self.isCancelled else {
                self.isFinished = true
                return
            }
            guard let block = self.block else {
                self.isFinished = true
                return
            }
            self.isExecuting = true
            block()
            self.isFinished = true
        }
    }
}

I was inspired by the gist

Inductive answered 19/5, 2022 at 14:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.