How to get hold of the currently executing NSOperation?

断了今生、忘了曾经 提交于 2019-12-08 15:36:41

问题


Is there an equivalent to [NSOperationQueue currentQueue] or [NSThread currentThread] for NSOperation?

I have a fairly complex domain model where the heavy processing happens quite deep down in the call stack. In order to timely cancel an operation I would need to pass the NSOperation as a parameter to every method until I get to the point where I want to interrupt a longer running loop. Using threads I could use [[NSThread currentThread] isCancelled] so it would seem convenient if there is an equivalent for NSOperation, unfortunately there is only the seemingly useless [NSOperationQueue currentQueue].


回答1:


Came up with an extension in swift that returns the running operations

extension NSOperationQueue {
    public var runningOperations: [NSOperation] {
        return operations.filter {$0.executing && !$0.finished && !$0.cancelled}
    }
}

You can then pick up the first one

if let operation = aQueue.runningOperations.first {}



回答2:


No, there's no method to find the currently executing operation.

Two ways to solve your problem:

  1. Operations are objects. If you need object A to talk to object B, you'll need to arrange for A to have a reference to B. There are lots of ways to do that. One way is to pass the operation along to each object that needs to know about it. Another is to use delegation. A third is to make the operation part of some larger "context" that's passed along to each method or function. If you find that you need to pass a reference from one object through several others just to get it to the object that will finally use it, that's a clue that you should think about rearranging your code.

  2. Have the "heavy lifting" method return some value that gets passed up the call chain. You don't necessarily need the heavy lifting method to call [currentOperation cancel] to accomplish your goal. In fact, it would be better to have it return some value that the operation will understand to mean "work is done, stop now" because it can check that return value and exit immediately rather than having to call -isCancelled once in a while to find out whether it has been cancelled.




回答3:


This isn't a good idea. Operations are usually canceled by their queue. Within the operation's main() method, you can periodically check if self is cancelled (say, every n trips through a loop, or at the start of every major block of commands) and abort if so.

To respond to a cancellation (say, some UI element tied to the operation's or queue's status), you use key value observing (KVO) to have your controller observe the operations' started, completion, and cancelled properties (as needed), then set your UI's state (always on the main thread) when those keys are updated. Per JeremyP's comments, it's important to note the KVO notifications come from the op's thread and UI should (almost) always be manipulated on the main thread, so you'll need to use -performSelectorOnMainThread... methods to update your actual UI when you receive a state change KVO note about your operations.

What are you really trying to do? That is, why do you feel other parts of your app need to know directly about the current operation?




回答4:


You could store the current operation in the thread dictionary. Just remember to get rid of it before you exit. You can safely use the thread dict if you created the object.




回答5:


You can use a combination of [NSOperationQueue currentQueue] & [NSThread currentThread] to accomplish this.

Essentially, you need to loop through the operations on the currentQueue and find the operation running on the currentThread.

NSOperation doesn't provide access to the thread it is running on, so you need to add that property yourself and assign it.

You're probably already subclassing NSOperation and providing a main, so add a 'thread' property to that subclass:

@interface MyOperation : NSOperation
    @property(nonatomic,strong) NSThread *thread ;
@end

Then, in your 'main' assign the current thread to that property

myOperation.thread = [NSThread currentThread]

You can then add a 'currentOperation' method:

+(MyOperation *)currentOperation
{
    NSOperationQueue *opQueue = [NSOperationQueue currentQueue] ;
    NSThread *currentThread = [NSThread currentThread] ;

    for( MyOperation *op in opQueue.operations ) {
        if( [op isExecuting] && [op respondsToSelector:@selector(thread)] ) {
                if( op.thread == currentThread ) {
                    return ( op ) ;
                }
            }
        }
    }

    return nil ;
}



回答6:


How do you know which operation you want to cancel? When you get to the point that you want to cancel, just call [myQueue operations] and go through the operations until you find ones that you now want to cancel. I guess if you have millions of operations (or thousands) this might not work.

[myQueue operations] is thread safe - a snapshot of the Queue contents. You can dive through it pretty quick cancelling at will.

Another way: NSOperationQueue is not a singleton, so you can create a Q that has say 200 jobs on it, and then cancel all 20 by just getting that Q and cancelling them all. Store the Q's in a dictionary on the main thread, and then you can get the jobs you want canceled from the dict and cancel them all. i.e. you have 1000 kinds of operations and at the point in the code where you realize you don't need a certain task, you just get the Q for that kind, and look through it for jobs to cancel.



来源:https://stackoverflow.com/questions/7336322/how-to-get-hold-of-the-currently-executing-nsoperation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!