Как заполучить выполняемую в настоящее время NSOperation?

Есть ли эквивалент [NSOperationQueue currentQueue] или же [NSThread currentThread] за NSOperation?

У меня довольно сложная модель предметной области, где тяжелая обработка происходит довольно глубоко в стеке вызовов. Для того, чтобы своевременно отменить операцию, мне необходимо пройти NSOperation в качестве параметра для каждого метода, пока не доберусь до точки, где я хочу прервать более продолжительный цикл. Используя темы, которые я мог бы использовать [[NSThread currentThread] isCancelled] так что было бы удобно, если есть эквивалент для NSOperation, к сожалению, есть только, казалось бы, бесполезный [NSOperationQueue currentQueue],

6 ответов

Придумал расширение в swift, которое возвращает запущенные операции

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

Вы можете забрать первый

if let operation = aQueue.runningOperations.first {}

Нет, нет способа найти выполняемую в данный момент операцию.

Два способа решить вашу проблему:

  1. Операции являются объектами. Если вам нужен объект A для связи с объектом B, вам нужно организовать, чтобы A имел ссылку на B. Есть много способов сделать это. Одним из способов является передача операции каждому объекту, который должен знать об этом. Другое - использовать делегирование. Третье - сделать операцию частью более крупного "контекста", который передается каждому методу или функции. Если вы обнаружите, что вам нужно передать ссылку от одного объекта через несколько других, просто чтобы передать ее объекту, который, наконец, будет его использовать, это ключ, который вы должны подумать о реорганизации своего кода.

  2. Пусть метод "тяжелой работы" вернет некоторое значение, которое передается по цепочке вызовов. Вам не обязательно нужен тяжелый метод для вызова [currentOperation cancel] для достижения вашей цели. На самом деле, было бы лучше, если бы он возвращал какое-то значение, которое будет понимать операция, что означает "работа выполнена, остановитесь сейчас", потому что она может проверить это возвращаемое значение и выйти сразу, а не вызывать -isCancelled время от времени, чтобы узнать, было ли оно отменено.

Это не очень хорошая идея. Операции обычно отменяются их очередью. В методе main() операции вы можете периодически проверять, отменяется ли self (скажем, каждый n отключается в цикле или в начале каждого основного блока команд), и прерывать работу, если это так.

Чтобы ответить на отмену (скажем, некоторый элемент пользовательского интерфейса, связанный с состоянием операции или очереди), вы используете наблюдение значения ключа (KVO), чтобы ваш контроллер наблюдал свойства запуска, завершения и отмены операций (при необходимости), а затем установите состояние вашего пользовательского интерфейса (всегда в главном потоке), когда эти ключи обновляются. Согласно комментариям JeremyP, важно отметить, что уведомления KVO поступают из потока операции, и пользовательский интерфейс должен (почти) всегда обрабатываться в основном потоке, поэтому вам нужно будет использовать методы -performSelectorOnMainThread... для обновления вашего реального пользовательского интерфейса, когда вы получить уведомление об изменении состояния KVO о вашей деятельности.

Что ты на самом деле пытаешься сделать? То есть, почему вы чувствуете, что другие части вашего приложения должны знать непосредственно о текущей операции?

Вы можете сохранить текущую операцию в словаре потоков. Просто не забудьте избавиться от него перед выходом. Вы можете безопасно использовать поток dict, если вы создали объект.

Вы можете использовать комбинацию [NSOperationQueue currentQueue] и [NSThread currentThread] для достижения этой цели.

По сути, вам нужно пройтись по операциям в currentQueue и найти операцию, выполняемую в currentThread.

NSOperation не предоставляет доступ к потоку, в котором он запущен, поэтому вам нужно добавить это свойство самостоятельно и назначить его.

Вы, вероятно, уже подклассифицируете NSOperation и предоставляете main, поэтому добавьте свойство 'thread' к этому подклассу:

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

Затем в вашем 'main' назначьте текущий поток этому свойству

myOperation.thread = [NSThread currentThread]

Затем вы можете добавить метод currentOperation:

+(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 ;
}

Как узнать, какую операцию вы хотите отменить? Когда вы дойдете до точки, которую хотите отменить, просто позвоните в [myQueue operations] и выполняйте операции, пока не найдете те, которые вы хотите отменить. Я думаю, если у вас есть миллионы операций (или тысячи), это может не сработать.

[Операции myQueue] являются потокобезопасными - это снимок содержимого очереди. Вы можете погрузиться в нее довольно быстро, отменив по желанию.

Другой способ: NSOperationQueue не является одиночным, поэтому вы можете создать Q, в котором есть 200 заданий, а затем отменить все 20, просто получив этот Q и отменив их все. Сохраните Q в словаре в главном потоке, и затем вы можете получить задания, которые вы хотите отменить из диктата, и отменить их все. то есть у вас есть 1000 видов операций, и в той точке кода, где вы понимаете, что вам не нужна определенная задача, вы просто получаете Q для этого вида и просматриваете его, чтобы найти задания для отмены.

Другие вопросы по тегам