如何取消相关操作

时间:2018-10-02 15:31:20

标签: swift nsoperationqueue nsoperation cancellation

  

请参阅底部的编辑。我相信我在此处发布的链接可以回答我的问题。

我正在编写一个需要对照相机和位置进行访问的iOS应用,以便用户可以拍摄照片并将照片与拍摄照片的CLLocation存储在一起。我决定实现一个基于操作的体系结构,在其中创建4个操作,每个操作取决于最后一个,以检查并执行以下步骤。更具体地说,每次用户想要拍照并上传照片时,我都请求访问相机和位置。

操作:
1. RequestCameraAccessOperation
2. RequestLocationWhenInUseOperation
3. TakePictureOperation
4. GetLocationOperation

依赖链:
2取决于1
3取决于2
4取决于3

我目前正在添加错误检查以及操作和相关操作的取消。现在,我正在通过在其main()的第一个操作结束时添加cancel()调用来测试取消。但是,没有任何从属操作正在取消。我正在检查他们的主要方法,以查看它们是否被取消,但这似乎无济于事。该文档说:

  

将操作添加到队列后,该操作将不再可用。队列接管并处理该任务的调度。但是,如果以后您决定根本不想执行该操作(例如,由于用户按下了进度面板中的“取消”按钮或退出了该应用程序),则可以取消该操作以防止其不必要地消耗CPU时间。通过调用操作对象本身的cancel()方法或调用OperationQueue类的cancelAllOperations()方法来执行此操作。   取消操作不会立即强制其停止正在执行的操作。尽管所有操作都希望尊重isCancelled属性中的值,但是您的代码必须显式检查此属性中的值并根据需要中止。

这是我的操作代码:

创建操作并设置依赖项

    // create operations
    let requestCameraAccessOp = RequestCameraAccessOperation()
    let requestLocationWhenInUseOp = RequestLocationWhenInUseOperation()
    let takePictureOp = TakePictureOperation(viewController: self)
    let locationOp = GetLocationOperation(locationManager: locationManager)

    locationOp.completionBlock = { [weak networkManager, unowned self] in
      // code that unwraps optionals and sends them to the networkManager
      self.dismiss(animated: true, completion: nil)
    }

    // set operation dependencies
    requestLocationWhenInUseOp.addDependency(requestCameraAccessOp)
    takePictureOp.addDependency(requestLocationWhenInUseOp)
    locationOp.addDependency(takePictureOp)

    // add operations to the queue
    operationQueue.addOperations([requestCameraAccessOp,
                                  requestLocationWhenInUseOp,
                                  takePictureOp,
                                  locationOp],
                                 waitUntilFinished: false)

第一次操作

class RequestCameraAccessOperation: Operation {
  let imagePicker = UIImagePickerController()
  var error: AVError.Code?

  override func main() {
    if isCancelled { return }

    let available = UIImagePickerController.isSourceTypeAvailable(.camera)
    let status = AVCaptureDevice.authorizationStatus(for: .video)

    switch (available, status) {
    case (false, _):
      error = AVError.contentIsUnavailable
    case (true, .authorized):
      break
    case (true, .notDetermined):
      AVCaptureDevice.requestAccess(for: .video) { accessGranted in
        if !accessGranted {
          self.error = AVError.applicationIsNotAuthorizedToUseDevice
        }
      }
    case (true, .restricted):
      fallthrough
    case (true, .denied):
      // present alert to the user to change authorization in settings
    case (true, _):
      error = AVError.unknown
    }

    // always cancel for now to test that cancellation works
    self.cancel()

//    if let error = error {
//      self.cancel()
//    }
  }
}

第二次操作

class RequestLocationWhenInUseOperation: Operation {
  var error: OperationError?

  override func main() {
    if isCancelled {
      cancel()
    }

    let enabled = CLLocationManager.locationServicesEnabled()
    let status = CLLocationManager.authorizationStatus()

    switch (enabled, status) {
    case (true, .authorizedWhenInUse):
      break
    case (true, .authorizedAlways):
      break
    default:
      // unsure of how to handle errors
      error = .failed("Error")
    }

//    if let error = error {
//      completion(.failed)
//    } else {
//      completion(.satisfied)
//    }
  }
}

第3和第4个操作具有相同的取消检查,因此我将避免冗余并跳过发布它们的代码。

据我所知,取消的原因为何不可行,有3种可能性。

  1. 我认为它不起作用。在第一个操作中调用cancel不会将相关操作标记为已取消,因此检查相关操作中的取消操作永远不会得出true。
  

编辑:阅读this后,看来我的假设是错误的,标记操作不会取消相关操作。

     
    

为此,最好使用每个组的队列将它们拆分。因此,为每个组在并发模式下创建一个NSOperation子类,包括一个队列,然后将每个子操作添加到该队列中。覆盖取消并调用[超级取消],然后调用[self.queue cancelAllOperations]。

  
  1. 我需要在operationQueue本身上调用cancelAllOperations。我不知道如何检查取消并在两次操作之间调用它。添加操作后,我无法控制operationQueue。这样做可能是可行的,但这似乎过于复杂,而不是“智能”解决方案。

  2. 此博客post描述了从上一个操作标记为完成后立即开始执行从属操作的位置。

我没看到什么?

0 个答案:

没有答案