取消包裹在NSOperation中的Alamofire请求会导致多个KVO? [英] Cancelling an Alamofire Request Wrapped In NSOperation Causes Multiple KVO?

查看:239
本文介绍了取消包裹在NSOperation中的Alamofire请求会导致多个KVO?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Xcode版本:6.3.2

Alamofire版本:1.2.2(通过Cocoapods安装)



为了设置 maxConcurrentOperationCount 以限制 NSOperationQueue 中的并发操作数,我将我 Alamofire 下载请求,只需像Rob建议。 / p>

NSOperation 的基本子类如下:

  class ConcurrentOperation:NSOperation {

override var concurrent:Bool {
return true
}

override var asynchronous :Bool {
return true
}

private var _executing:Bool = false
override var executing:Bool {
get {
return _executing
}
set {
if(_executing!= newValue){
self.willChangeValueForKey(isExecuting)
_executing = newValue
self.didChangeValueForKey (isExecuting)
}
}
}

private var _finished:Bool = false;
override var finished:Bool {
get {
return _finished
}
set {
if(_finished!= newValue){
self .willChangeValueForKey(isFinished)
_finished = newValue
self.didChangeValueForKey(isFinished)
}
}
}

/ //完成操作
///
///这将导致isFinished和isExecuting的适当KVN

func completeOperation(){
executing = false
finished = true
}

override func start(){
if(canceled){
finished = true
return
}

executing = true

main()
}
}

我的子类包装一个Alamofire下载请求,如下:

  class DownloadImageOperation :ConcurrentOperation {
let URLString:String
let downloadImageCompletionHandler:(responseObject:AnyObject ?, error:NSError?) - > ()
weak var request:Alamofire.Request?

init(URLString:String,downloadImageCompletionHandler:(responseObject:AnyObject ?, error:NSError?) - >()){
self.URLString = URLString
self.downloadImageCompletionHandler = downloadImageCompletionHandler
super.init()
}

override func main(){
let destination = Alamofire.Request.suggestedDownloadDestination(directory:.DocumentDirectory,domain: .UserDomainMask)
request = Alamofire.download(.GET,URLString,destination).response {(request,response,responseObject,error)in
if self.cancelled {
println(Alamofire 。
} $ {
self.downloadImageCompletionHandler(responseObject:responseObject,error:error)
}
self.completeOperation()
}
}

override func cancel(){
request?.cancel()
super.cancel()
}
}

它覆盖 cancel()当取消 NSOperation 时取消Alamofire请求。



我使用KVO观察器观察 NSOperationQueue 的完成情况。

  private var testAlamofireContext = 0 

class TestAlamofireObserver:NSObject {
var queue = NSOperationQueue ()

init(delegate:ImageDownloadDelegate){
super.init()
queue.addObserver(self,forKeyPath:operations,options:.New,context:& ; testAlamofireContext)
}

deinit {
queue.removeObserver(self,forKeyPath:operations,context:& testAlamofireContext)
}
b $ b override func observeValueForKeyPath(keyPath:String,ofObject object:AnyObject,change:[NSObject:AnyObject],context:UnsafeMutablePointer< Void>){
if context ==& testAlamofireContext {
if self .queue.operations.count == 0 {
println(Image Download Complete queue。keyPath:\(keyPath); object:\(object); context:\(context))
}
} else {
super.observeValueForKeyPath(keyPath,ofObject:object,change:change,context:context)
}
}
}

我开始下载这样的列表:

  func downloadImages(){
let imgLinks = [
https://farm4.staticflickr.com/3925/18769503068_1fc09427ec_k.jpg,
https: //farm1.staticflickr.com/338/18933828356_4f57420df7_k.jpg,
https://farm4.staticflickr.com/3776/18945113685_ccec89d67a_o.jpg,
https://farm1.staticflickr。 com / 366 / 18333992053_725f21166e_k.jpg,
https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg,
https://farm1.staticflickr.com/373/18930501406_4753ac021a_k。 jpg,
https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg,
https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg,
https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg,
https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg,
https:// farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg,
https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg,
https://farm1.staticflickr.com/ 461 / 18949863812_ddf700bd03_o.jpg,
https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg,
https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg ,
https://farm1.staticflickr.com/384/18955290345_fb93d17828_o.jpg,
https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg,
https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg,
https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg,
https:// farm1。 staticflickr.com/283/18772907409_56ffbe573b_k.jpg,
https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg,
https://farm1.staticflickr.com/502/ 18949263495_88d75d2d2f_k.jpg,
https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg,
https://farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg,
https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg,
https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg,
https: //farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg,
https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg,
https://farm1.staticflickr。 com / 366 / 18333992053_725f21166e_k.jpg,
https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg,
https://farm1.staticflickr.com/373/18930501406_4753ac021a_k。 jpg,
https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg,
https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg,
https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg,
https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg,
https:// farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg,
https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg,
https://farm1.staticflickr.com/ 461 / 18949863812_ddf700bd03_o.jpg,
https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg,
https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg ,
https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg,
https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg,
https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg,
https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg,
https:// farm1。 staticflickr.com/502/18949263495_88d75d2d2f_k.jpg,
https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg,
https://farm1.staticflickr.com/356/ 18957923475_3dc9df7634_k.jpg,
https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg,
https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg,
https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg,
https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg,
https: //farm1.staticflickr.com/266/18956724112_6e61a743a5_k.jpg
]

var testAlamofireObserver = TestAlamofireObserver()
testAlamofireObserver!.queue.maxConcurrentOperationCount = 5

imgLink中的imgLink {
let operation = DownloadImageOperation(URLString:imgLink){
(responseObject,error)in

if responseObject == nil {
// handle error
println(failed:\(error))
} else {
println(\(responseObject?.absoluteString)downloaded。)
}
}
testAlamofireObserver!.queue.addOperation(operation)
}
}

如果队列未完成取消,日志输出应为:

  06-22 17:11:04.206 RSS壁纸Switchr [46250:714702]可选(可选(https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg))下载。 
...
...
...
2015-06-22 17:11:56.979 RSS壁纸Switchr [46250:714702]可选( //farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg))下载。
2015-06-22 17:11:56.979 RSS壁纸Switchr [46250:714702]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x6180002354a0> {name ='NSOperationQueue 0x6180002354a0'};上下文:0x000000010007eb70



如果队列接收到 cancelAllOperations(),日志输出应为:

  2015-06-22 17:16:29.691 RSS壁纸Switchr [46467:720630 ]可选(可选(https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg))下载。 
2015-06-22 17:16:32.632 RSS壁纸Switchr [46467:720630] Alamofire.download取消了下来。不继续。
...
...
2015-06-22 17:16:32.642 RSS壁纸Switchr [46467:720630] Alamofire.download取消了下来。不继续。
2015-06-22 17:16:32.643 RSS壁纸Switchr [46467:720630]图片下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x600000024c20> {name ='NSOperationQueue 0x600000024c20'};上下文:0x000000010007eb70

但是,如果我更改了 maxConcurrentOperationCount 到上面的非默认值,并且队列接收 cancelAllOperations(),日志变为:

  2015-06-22 17:17:56.427 RSS壁纸Switchr [46606:722523]可选(可选(https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg))下载。 
2015-06-22 17:17:58.675 RSS壁纸Switchr [46606:722523] Alamofire.download已取消,当downlading。不继续。
...
...
2015-06-22 17:17:58.677 RSS壁纸Switchr [46606:722523] Alamofire.download取消了下来。不继续。
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722720]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722560]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722574]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722719]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722721]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70
2015-06-22 17:17:58.678 RSS壁纸Switchr [46606:722572]图像下载完成队列。 keyPath:operations; object:< NSOperationQueue:0x608000424ee0> {name ='NSOperationQueue 0x608000424ee0'};上下文:0x000000010007eb70

已执行KVO observeValueForKeyPath 从多个不同的线程。线程的数量可以是可变的。这将导致KVO的完成功能被执行多次。如果我不改变默认值 maxConcurrentOperationCount 或不要请求,则不会发生这种情况?.cancel() Alamofire.Request



为什么我关心KVO完成功能的多次执行?我的目的是启动一个下载队列,当足够的下载完成,取消剩余的操作,甚至是非开始或下载,然后做一些下载。完成函数应该只执行一次,并且两个因素(1)改变默认值 maxConcurrentOperationCount (2)不要请求?.cancel ) Alamofire.Request 可能与它相关。

解决方案

我没有找到您描述的多个KVN行为那令人惊讶。文档中没有任何内容说当取消所有操作时,会导致操作上的单个KVN。事实上,可以安全地推断你描述的行为应该是期望的(因为它不会抢占所有这些工作线程,而是发送一个 cancel 消息给每个,并且每个操作都负责在自己的时间响应它;并且我不会期望操作被更新,直到操作最终实际完成)。



就我个人而言,我建议完全退出这个观察者模式。您的代码不应该取决于 NSOperationQueue 是否一次删除所有操作。我宁愿建议你改用现有的 downloadImageCompletionHandler 闭包,调用它是否请求完成。只要让闭包看看错误对象,以确定它是否被取消或是否由于某些其他原因失败。






如果你的意图是知道这些操作何时完成,我不会依赖操作 KVN。相反,我可能创建一个完成操作,依赖于所有其他请求:

  let completionOperation = NSBlockOperation创建完成操作
//做任何你想要的操作
}

imgLinks中的imgLink {
let operation = DownloadImageOperation(URLString:imgLink){responseObject,error in
if error!= nil {
if error!.code == NSURLErrorCancelled&&错误!.domain == NSURLErrorDomain {
println(everything OK,just cancelled)
} else {
println(error = \(error))
}
}
if responseObject!= nil {
println(\(responseObject?.absoluteString)downloaded。)
}
}

completionOperation.addDependency(operation)//添加依赖性

testAlamofireObserver!.queue.addOperation(operation)
}

NSOperationQueue.mainQueue()。addOperation completionOperation)//在其他队列上调度完成操作(所以当我取消那个队列上的所有内容时,我也不会取消这个操作)


My Xcode version: 6.3.2
Alamofire version: 1.2.2 (installed via Cocoapods)

In order to set maxConcurrentOperationCount to limit the concurrent operation number in a NSOperationQueue, I wrap my Alamofire download request in a NSOperation just like Rob suggested.

The basic subclass of NSOperation like this:

class ConcurrentOperation : NSOperation {

    override var concurrent: Bool {
        return true
    }

    override var asynchronous: Bool {
        return true
    }

    private var _executing: Bool = false
    override var executing: Bool {
        get {
            return _executing
        }
        set {
            if (_executing != newValue) {
                self.willChangeValueForKey("isExecuting")
                _executing = newValue
                self.didChangeValueForKey("isExecuting")
            }
        }
    }

    private var _finished: Bool = false;
    override var finished: Bool {
        get {
            return _finished
        }
        set {
            if (_finished != newValue) {
                self.willChangeValueForKey("isFinished")
                _finished = newValue
                self.didChangeValueForKey("isFinished")
            }
        }
    }

    /// Complete the operation
    ///
    /// This will result in the appropriate KVN of isFinished and isExecuting

    func completeOperation() {
        executing = false
        finished  = true
    }

    override func start() {
        if (cancelled) {
            finished = true
            return
        }

        executing = true

        main()
    }
}

And my subclass wrapping an Alamofire download request like this:

class DownloadImageOperation : ConcurrentOperation {
    let URLString: String
    let downloadImageCompletionHandler: (responseObject: AnyObject?, error: NSError?) -> ()
    weak var request: Alamofire.Request?

    init(URLString: String, downloadImageCompletionHandler: (responseObject: AnyObject?, error: NSError?) -> ()) {
        self.URLString = URLString
        self.downloadImageCompletionHandler = downloadImageCompletionHandler
        super.init()
    }

    override func main() {
        let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
        request = Alamofire.download(.GET, URLString, destination).response { (request, response, responseObject, error) in
            if self.cancelled {
                println("Alamofire.download cancelled while downlading. Not proceed.")
            } else {
                self.downloadImageCompletionHandler(responseObject: responseObject, error: error)
            }
            self.completeOperation()
        }
    }

    override func cancel() {
        request?.cancel()
        super.cancel()
    }
}

It overrides cancel() and tries to cancel the Alamofire request when the NSOperation is cancelled.

I used a KVO observer to watch the completion of NSOperationQueue.

private var testAlamofireContext = 0

class TestAlamofireObserver: NSObject {
    var queue = NSOperationQueue()

    init(delegate: ImageDownloadDelegate) {
        super.init()
        queue.addObserver(self, forKeyPath: "operations", options: .New, context: &testAlamofireContext)
    }

    deinit {
        queue.removeObserver(self, forKeyPath: "operations", context: &testAlamofireContext)
    }

    override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject: AnyObject], context: UnsafeMutablePointer<Void>) {
        if context == &testAlamofireContext {
            if self.queue.operations.count == 0 {
                println("Image Download Complete queue. keyPath: \(keyPath); object: \(object); context: \(context)")
            }
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }
}

I started a list of downloading like this:

func downloadImages() {
    let imgLinks = [
        "https://farm4.staticflickr.com/3925/18769503068_1fc09427ec_k.jpg",
        "https://farm1.staticflickr.com/338/18933828356_4f57420df7_k.jpg",
        "https://farm4.staticflickr.com/3776/18945113685_ccec89d67a_o.jpg",
        "https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg",
        "https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg",
        "https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg",
        "https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg",
        "https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg",
        "https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg",
        "https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg",
        "https://farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg",
        "https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg",
        "https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg",
        "https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg",
        "https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg",
        "https://farm1.staticflickr.com/384/18955290345_fb93d17828_o.jpg",
        "https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg",
        "https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg",
        "https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg",
        "https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg",
        "https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg",
        "https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg",
        "https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg",
        "https://farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg",
        "https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg",
        "https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg",
        "https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg",
        "https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg",
        "https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg",
        "https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg",
        "https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg",
        "https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg",
        "https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg",
        "https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg",
        "https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg",
        "https://farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg",
        "https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg",
        "https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg",
        "https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg",
        "https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg",
        "https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg",
        "https://farm1.staticflickr.com/373/18930501406_4753ac021a_k.jpg",
        "https://farm1.staticflickr.com/283/18772907409_56ffbe573b_k.jpg",
        "https://farm1.staticflickr.com/314/18940901785_b0564b1c9b_o.jpg",
        "https://farm1.staticflickr.com/502/18949263495_88d75d2d2f_k.jpg",
        "https://farm4.staticflickr.com/3912/18938184302_6e0ca9ad31_k.jpg",
        "https://farm1.staticflickr.com/356/18957923475_3dc9df7634_k.jpg",
        "https://farm1.staticflickr.com/378/18925014986_e87feca9c7_o.jpg",
        "https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg",
        "https://farm1.staticflickr.com/303/18920711216_4684ff4295_k.jpg",
        "https://farm1.staticflickr.com/558/18935058546_fc10d10855_k.jpg",
        "https://farm1.staticflickr.com/266/18956724112_6e61a743a5_k.jpg"
    ]

    var testAlamofireObserver = TestAlamofireObserver()
    testAlamofireObserver!.queue.maxConcurrentOperationCount = 5

    for imgLink in imgLinks {
        let operation = DownloadImageOperation(URLString: imgLink) {
            (responseObject, error) in

            if responseObject == nil {
                // handle error here
                println("failed: \(error)")
            } else {
                println("\(responseObject?.absoluteString) downloaded.")
            }
        }
        testAlamofireObserver!.queue.addOperation(operation)
    }
}

If the queue completed without receiving any cancellation, the log outputs should be:

2015-06-22 17:11:04.206 RSS Wallpaper Switchr[46250:714702] Optional(Optional("https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg")) downloaded.
...
...
...
2015-06-22 17:11:56.979 RSS Wallpaper Switchr[46250:714702] Optional(Optional("https://farm1.staticflickr.com/461/18949863812_ddf700bd03_o.jpg")) downloaded.
2015-06-22 17:11:56.979 RSS Wallpaper Switchr[46250:714702] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x6180002354a0>{name = 'NSOperationQueue 0x6180002354a0'}; context: 0x000000010007eb70

If the queue receives cancelAllOperations(), the log outputs should be:

2015-06-22 17:16:29.691 RSS Wallpaper Switchr[46467:720630] Optional(Optional("https://farm1.staticflickr.com/366/18333992053_725f21166e_k.jpg")) downloaded.
2015-06-22 17:16:32.632 RSS Wallpaper Switchr[46467:720630] Alamofire.download cancelled while downlading. Not proceed.
...
...
2015-06-22 17:16:32.642 RSS Wallpaper Switchr[46467:720630] Alamofire.download cancelled while downlading. Not proceed.
2015-06-22 17:16:32.643 RSS Wallpaper Switchr[46467:720630] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x600000024c20>{name = 'NSOperationQueue 0x600000024c20'}; context: 0x000000010007eb70

However, if I changed maxConcurrentOperationCount to non-default value as above, and the queue receives cancelAllOperations(), the log became:

2015-06-22 17:17:56.427 RSS Wallpaper Switchr[46606:722523] Optional(Optional("https://farm4.staticflickr.com/3777/18962702032_086453ee7a_k.jpg")) downloaded.
2015-06-22 17:17:58.675 RSS Wallpaper Switchr[46606:722523] Alamofire.download cancelled while downlading. Not proceed.
...
...
2015-06-22 17:17:58.677 RSS Wallpaper Switchr[46606:722523] Alamofire.download cancelled while downlading. Not proceed.
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722720] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722560] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722574] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722719] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722721] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70
2015-06-22 17:17:58.678 RSS Wallpaper Switchr[46606:722572] Image Download Complete queue. keyPath: operations; object: <NSOperationQueue: 0x608000424ee0>{name = 'NSOperationQueue 0x608000424ee0'}; context: 0x000000010007eb70

The KVO observeValueForKeyPath was executed from multiple different threads. The number of threads may be variable. This will result the completion function of KVO to be executed several times. And this condition does not happen if I do not change the default of maxConcurrentOperationCount or do not request?.cancel() for Alamofire.Request.

Why do I care about more than one executions of the KVO completion function? My purpose is to start a download queue, when enough downloads complete, cancel the remaining operations, even non-started or in downloading, and then do something for the downloads. The completion function is supposed to execute only once, and two factors (1) change the default of maxConcurrentOperationCount (2) do not request?.cancel() for Alamofire.Request may be related to it. I'd like to know why and how to correct this.

解决方案

I don't find the multiple KVN behavior you describe as that surprising. There's nothing in the documentation that says that when it cancels all operations, that a single KVN on operations will result. In fact, one might safely infer that the behavior you describe should be expected (because it doesn't preemptively kill all of those worker threads, but rather sends a cancel message to each, and each operation is responsible for responding to that in its own time; and I wouldn't expect operations to be updated until the operation finally actually finishes).

Personally, I would advise retiring this observer pattern entirely. Your code should not be contingent upon whether NSOperationQueue removes all of operations at once or not. I would instead suggest that you instead rely upon your existing downloadImageCompletionHandler closure, calling it whether the request completed or not. Just have the closure look at the error object to figure out whether it was canceled or whether it failed for some other reason.


If your intent is to know when all of these operations are done, I wouldn't rely on operations KVN. Instead, I might create a completion operation, dependent upon all of those other requests:

let completionOperation = NSBlockOperation() {                    // create completion operation
    // do whatever you want here
}

for imgLink in imgLinks {
    let operation = DownloadImageOperation(URLString: imgLink) { responseObject, error in
        if error != nil {
            if error!.code == NSURLErrorCancelled && error!.domain == NSURLErrorDomain {
                println("everything OK, just canceled")
            } else {
                println("error=\(error)")
            }
        }
        if responseObject != nil {
            println("\(responseObject?.absoluteString) downloaded.")
        }
    }

    completionOperation.addDependency(operation)                 // add dependency

    testAlamofireObserver!.queue.addOperation(operation)
}

NSOperationQueue.mainQueue().addOperation(completionOperation)   // schedule completion operation on some other queue (so that when I cancel everything on that other queue, I don't cancel this, too)

这篇关于取消包裹在NSOperation中的Alamofire请求会导致多个KVO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆