NSOperationQueue添加操作并等待完成

4

你好,我正在使用Swift构建一个应用程序。我需要按照特定顺序处理通知。因此,我正在尝试使用addOperations waitUntilFinished。

这是我的操作:

let oldify = NSOperation()
    oldify.completionBlock = {
println("oldify")
}
let appendify = NSOperation()
    appendify.completionBlock = {
println("appendify")
}
let nettoyify = NSOperation()
    nettoyify.completionBlock = {
println("nettoyify")
}
NSOperationQueue.mainQueue().maxConcurrentOperationCount = 1
NSOperationQueue.mainQueue().addOperations([oldify, appendify, nettoyify], waitUntilFinished: true)

使用这段代码时,没有任何操作被执行。如果我改用以下代码:

NSOperationQueue.mainQueue().maxConcurrentOperationCount = 1
NSOperationQueue.mainQueue().addOperation(oldify)
NSOperationQueue.mainQueue().addOperation(appendify)
NSOperationQueue.mainQueue().addOperation(nettoyify)

操作已执行,但顺序不正确。

有人知道我做错了什么吗?我在Swift上变得自信了,但对NSOperations完全是新手。


可能是 NSOperationQueue serial FIFO queue 的重复问题。 - Azat
1个回答

6
一些问题:
  1. You are examining behavior of the completion block handlers. As the completionBlock documentation says:

    The exact execution context for your completion block is not guaranteed but is typically a secondary thread. Therefore, you should not use this block to do any work that requires a very specific execution context.

    The queue will manage the operations themselves, but not their completion blocks (short of making sure that the the operation finishes before its completionBlock is started). So, bottom line, do not make any assumptions about (a) when completion blocks are run, (b) the relation of one operation's completionBlock to other operations or their completionBlock blocks, etc., nor (c) which thread they are performed on.

  2. Operations are generally executed in the order in which they were added to the queue. If you add an array of operations, though, the documentation makes no formal assurances that they are enqueued in the order they appear in that array. You might, therefore, want to add the operations one at a time.

  3. Having said that, the documentation goes on to warn us:

    An operation queue executes its queued operation objects based on their priority and readiness. If all of the queued operation objects have the same priority and are ready to execute when they are put in the queue—that is, their isReady method returns YES—they are executed in the order in which they were submitted to the queue. However, you should never rely on queue semantics to ensure a specific execution order of operation objects. Changes in the readiness of an operation can change the resulting execution order. If you need operations to execute in a specific order, use operation-level dependencies as defined by the NSOperation class.

    To establish explicit dependencies, you might do something like:

    let oldify = NSBlockOperation() {
        NSLog("oldify")
    }
    oldify.completionBlock = {
        NSLog("oldify completion")
    }
    
    let appendify = NSBlockOperation() {
        NSLog("appendify")
    }
    appendify.completionBlock = {
        NSLog("appendify completion")
    }
    
    appendify.addDependency(oldify)
    
    let nettoyify = NSBlockOperation() {
        NSLog("nettoyify")
    }
    nettoyify.completionBlock = {
        NSLog("nettoyify completion")
    }
    
    nettoyify.addDependency(appendify)
    
    let queue = NSOperationQueue()
    queue.addOperations([oldify, appendify, nettoyify], waitUntilFinished: false)
    
  4. BTW, as you'll see above, you should not add operations to the main queue in conjunction with the waitUntilFinished. Feel free to add them to a different queue, but don't dispatch from a serial queue, back to itself, with the waitUntilFinished option.


非常感谢您,@Rob。现在对我来说看起来更清晰了。尽管如此,我仍然面临一个问题。在appendify块中,我正在使用findObjectsInBackgroundWithBlock在parse上运行请求。结果是oldify、appendify和nettoyify按正确顺序执行,但请求(应该在appendify内部)在所有事情之后返回结果。我正在尝试在主线程上执行该请求,因为我已经设置了queue.qualityOfService = NSQualityOfService.UserInitiated - Quentin Malgaud
你的操作正在使用 findObjectsInBackgroundWithBlock 吗?如果是这样,那么这是一个异步请求,所以我猜你的操作很可能会在 Parse 查询完成之前就已经完成了。有两种解决方案:要么将它们包装在异步的 NSOperation 子类中,只有当请求完成时才手动完成操作,要么让你的操作发出同步请求(但一定要将这些操作添加到你自己的队列中,而不是主队列)。 - Rob
非常感谢@Rob。我选择使用.find()进行同步请求,而不是findObjectsInBackgroundWithBlock。不过我必须将waitUntilFinished设置为true。由于所有这些操作都不在主队列上执行,所以这会有问题吗? - Quentin Malgaud
为什么需要waitUntilFinished?将任何依赖于这些基于“find”的操作放入另一个操作中,并使其依赖于其他操作。除非绝对必要(在这里不是),否则您真的不想使用waitUntilFinished。回答您的问题,无论操作在后台线程上执行什么操作,都不会从主线程调用waitUntilFinished - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接