Почему моя NSOperation не отменяется?

У меня есть этот код для добавления экземпляра NSOperation в очередь

let operation = NSBlockOperation()
operation.addExecutionBlock({
    self.asyncMethod() { (result, error) in
        if operation.cancelled {
            return
        }

        // etc
    }
})
operationQueue.addOperation(operation)

Когда пользователь покидает представление, которое вызвало этот приведенный выше код, я отменяю операцию, выполняя

operationQueue.cancelAllOperations()

При тестировании отмены я на 100% уверен, что отмена выполняется до возврата асинхронного метода, поэтому я ожидаю, что operation.cancelled будет истинным. К сожалению, этого не происходит, и я не могу понять, почему

Я выполняю отмену viewWillDisappear

ИЗМЕНИТЬ

asyncMethod содержит сетевую операцию, которая выполняется в другом потоке. Вот почему существует обратный вызов: для обработки возвратов сетевых операций. Сетевая операция выполняется глубоко в иерархии классов, но я хочу обрабатывать NSOperations на корневом уровне.


person StackOverflower    schedule 29.10.2015    source источник
comment
вы должны быть уверены, что operation.cancelled истинно перед тестом, а не только перед возвратом асинхронного метода...   -  person user3441734    schedule 30.10.2015
comment
@ user3441734: что вы имеете в виду под тестом? Невозможно отменить операцию до запуска асинхронного метода, потому что нет логики. Если его отменить раньше, он просто не запустится вообще.   -  person StackOverflower    schedule 30.10.2015
comment
Я на 100% уверен, что отмена выполняется до возврата асинхронного метода, поэтому я ожидаю, что operation.cancelled будет истинным. асинхронный метод вернется, даже если operation.cancelled имеет значение false. он просто не возвращается «рано».   -  person user3441734    schedule 30.10.2015
comment
@ user3441734: извините, но я не понимаю вашей точки зрения. Я не ожидаю, что асинхронный метод вернется раньше. Я просто хочу избежать выполнения логики обратного вызова через operation.cancelled проверку.   -  person StackOverflower    schedule 30.10.2015
comment
избежать казни? это ваша ответственность .... для этой цели вы проверяете, является ли operation.cancelled истинным или ложным, не так ли? за этим нет чуда, если Operation.cancelled имеет значение true, не продолжайте задание (вы можете проверять его снова и снова... везде в своем коде и возвращаться с задания (завершить его) "рано"   -  person user3441734    schedule 30.10.2015
comment
Вы задаете неправильный вопрос. Нет никакой гарантии, что проверка, которую вы выполняете в блоке выполнения, всегда выполняется до того, как переменная cancelled будет установлена ​​в YES. Использование точек останова и отладчика не помогает понять, что там происходит. Вы невинная жертва условий гонки :)   -  person HepaKKes    schedule 08.11.2015


Ответы (5)


Вызов метода отмены этого объекта устанавливает значение этого свойства в YES. После отмены операция должна перейти в состояние завершения.

Отмена операции не останавливает выполнение кода получателя. Объект операции отвечает за периодический вызов этого метода и самоостановку, если метод возвращает YES.

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

import Foundation

let operation1 = NSBlockOperation()
let operation2 = NSBlockOperation()
let queue = NSOperationQueue()
operation1.addExecutionBlock { () -> Void in
    repeat {
        usleep(10000)
        print(".", terminator: "")
    } while !operation1.cancelled
}
operation2.addExecutionBlock { () -> Void in
    repeat {
        usleep(15000)
        print("-", terminator: "")
    } while !operation2.cancelled
}
queue.addOperation(operation1)
queue.addOperation(operation2)
sleep(1)
queue.cancelAllOperations()

попробуйте этот простой пример на детской площадке.

если действительно важно запустить другой асинхронный код, попробуйте это

operation.addExecutionBlock({
if operation.cancelled {
            return
        }    
self.asyncMethod() { (result, error) in


        // etc
    }
})
person user3441734    schedule 30.10.2015
comment
Пожалуйста, прочитайте мой вопрос правильно. Я проверяю значение cancelled. Моя проблема в том, что всегда false, даже когда я отменил операцию - person StackOverflower; 31.10.2015
comment
если ваш self.asyncMethod() действительно является чем-то асинхронным, он немедленно возвращается, и блок выполнения также немедленно завершается. я обновил свой ответ очень простым примером использования NSOperation и NSOperationQueue - person user3441734; 01.11.2015
comment
Я ценю, что вы собрали все это вместе, но я не вижу никакой разницы между моим кодом и вашим. Я включил отладчик после отмены операции и обратного вызова асинхронного метода, я проверил, что отмена выполняется до if operation.cancelled , но отмена остается в false - person StackOverflower; 02.11.2015
comment
почему вы выполняете другой асинхронный код внутри операционного блока? в очереди ведьм этот код работает? в вашем закрытии значение отражает захваченное значение operation.cancelled и никогда не изменится... если необходимо запустить этот код «как есть», вы должны проверить значение operation.cancelled, прежде чем отправлять задание в другую очередь - person user3441734; 02.11.2015
comment
доступ асинхронного метода к сетевым ресурсам через сторонний компонент, и я не могу это контролировать. Мне нужно проверить статус операции внутри обратного вызова или поискать другой подход. В моем сценарии нет смысла проверять статус перед вызовом асинхронного метода. Спасибо - person StackOverflower; 02.11.2015
comment
в вашем примере это было в самом начале... в чем разница? если вы проверите отменено перед отправкой задания, задание не будет отправлено, если ваша операция будет отменена. какое поведение вам нужно? - person user3441734; 02.11.2015

это потому, что вы делаете работу неправильно. Вы отменяете операцию после ее выполнения. Проверьте этот код, блок выполняется в одном фоновом потоке. Перед началом выполнения – отмена операции, удаление первого блока из очереди.

Свифт 4

let operationQueue = OperationQueue()
operationQueue.qualityOfService = .background

let ob1 = BlockOperation {
    print("ExecutionBlock 1. Executed!")
}

let ob2 = BlockOperation {
    print("ExecutionBlock 2. Executed!")
}

operationQueue.addOperation(ob1)
operationQueue.addOperation(ob2)

ob1.cancel()

// ExecutionBlock 2. Executed!

Свифт 2

let operationQueue = NSOperationQueue()
operationQueue.qualityOfService = .Background

let ob1 = NSBlockOperation()
ob1.addExecutionBlock {
    print("ExecutionBlock 1. Executed!")
}

let ob2 = NSBlockOperation()
ob2.addExecutionBlock {
    print("ExecutionBlock 2. Executed!")
}

operationQueue.addOperation(ob1)
operationQueue.addOperation(ob2)

ob1.cancel()

// ExecutionBlock 2. Executed!
person dimpiax    schedule 07.11.2015

Операция не ждет завершения вашего asyncMethod. Поэтому он немедленно возвращается, если вы добавите его в Очередь. И это потому, что вы заключаете свою асинхронную сетевую операцию в асинхронный файл NSOperation.

NSOperation предназначен для более сложной асинхронной обработки вместо простого вызова performSelectorInBackground. Это означает, что NSOperation используется для перевода сложных и длительных операций в фоновый режим, а не для блокировки основного потока. Хорошую статью о обычно используемом NSOperation можно найти здесь:

http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues

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

person charlyatwork    schedule 08.11.2015

Не имеет смысла помещать асинхронную функцию в блок с NSBlockOperation. Что вам, вероятно, нужно, так это правильный подкласс NSOperation в качестве параллельной операции, которая выполняет асинхронную рабочую нагрузку. Однако создать подкласс NSOperation правильно не так просто, как хотелось бы.

Вы можете посмотреть здесь многоразовый подкласс для NSOperation для примера реализации.

person CouchDeveloper    schedule 09.11.2015

Я не уверен на 100%, что вы ищете, но, может быть, вам нужно передать операцию в качестве параметра в asyncMethod() и проверить там состояние отмены?

operation.addExecutionBlock({
  asyncMethod(operation) { (result, error) in
  // Result code
  }
})
operationQueue.addOperation(operation)

func asyncMethod(operation: NSBlockOperation, fun: ((Any, Any)->Void)) {
  // Do stuff...
  if operation.cancelled {
    // Do something...
    return // <- Or whatever makes senes
  }
}
person Mikael Hellman    schedule 06.11.2015