Das Abbrechen einer in NSOperation eingewickelten Alamofire-Anforderung verursacht mehrere KVOs?

Meine Xcode-Version: 6.3.2
Alamofire Version: 1.2.2 (über Cocoapods installiert)

Um @ zu setzmaxConcurrentOperationCount, um die Anzahl gleichzeitiger Vorgänge in einem @ zu begrenzeNSOperationQueue, Ich wickle mein Alamofire Download-Anfrage in einer NSOperation nur wie Rob vorgeschlagen.

Die grundlegende Unterklasse vonNSOperation so was

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()
    }
}

Und meine Unterklasse schließt eine Alamofire-Download-Anfrage wie folgt ab:

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 überschreibtcancel() und versucht, die Alamofire-Anforderung abzubrechen, wenn dasNSOperation ist storniert

Ich habe einen KVO-Beobachter verwendet, um die Fertigstellung von @ zu verfolgeNSOperationQueue.

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)
        }
    }
}

Ich habe eine Liste mit Downloads wie folgt gestartet:

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)
    }
}

Wenn die Warteschlange ohne Abbruch beendet wurde, sollten die Protokollausgaben wie folgt lauten:

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

Wenn die Warteschlange @ empfäncancelAllOperations(), die Logausgaben sollten sein:

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

Allerdings, wenn ich @ geändert hamaxConcurrentOperationCount auf einen nicht standardmäßigen Wert wie oben und die Warteschlange erhältcancelAllOperations(), das Protokoll wurde:

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 KVOobserveValueForKeyPath wurde von mehreren verschiedenen Threads ausgeführt. Die Anzahl der Threads kann variabel sein. Dies führt dazu, dass die Abschlussfunktion von KVO mehrmals ausgeführt wird. Und diese Bedingung tritt nicht auf, wenn ich die Standardeinstellung von @ nicht ändermaxConcurrentOperationCount oder nichtrequest?.cancel() zumAlamofire.Request.

Warum interessieren mich mehr als eine Ausführung der KVO-Vervollständigungsfunktion? Mein Ziel ist es, eine Download-Warteschlange zu starten, wenn genügend Downloads abgeschlossen sind, die verbleibenden Vorgänge abzubrechen, auch wenn sie noch nicht gestartet oder heruntergeladen wurden, und dann etwas für die Downloads zu tun. Die Vervollständigungsfunktion soll nur einmal ausgeführt werden, und zwei Faktoren (1) ändern den Standardwert vonmaxConcurrentOperationCount (2) nichtrequest?.cancel() zumAlamofire.Request kann damit zusammenhängen. Ich möchte wissen, warum und wie ich das korrigieren kann.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage