sloeberGJ
sloeberGJ

Reputation: 385

NSOperation running on main thread

I've used this guide to make my app use NSOperationQueue and NSOperations. But my code still runs on the main thread regardless :S? I must say that I'm new to NSOperation, I think it is a small thing that I missed.

class CountryFetcher: NSObject{
    var operation: NSOperation?
    var alamoFireQueue: NSOperationQueue{
        let val = NSOperationQueue()
        val.maxConcurrentOperationCount = 10
        val.name = "Alamofire Downloading Queue"
        return val
    }

    func getCountries(){
        operation = CountryProcessor(URLString: (BASE_URL + "countries/"+CAT_STAMPS))
        alamoFireQueue.addOperation(operation!)
    }
}

class CountryProcessor : ConcurrentOperation {
    let URLString: String
    weak var request: Alamofire.Request?


    init(URLString: String) {
        self.URLString = URLString
        super.init()
    }

    override func main() {
        request = Alamofire.request(.GET, URLString).responseJSON { response in
            if let dataResponse = response.data{
                var test: NSArray?
                do{
                    test = try NSJSONSerialization.JSONObjectWithData(dataResponse, options: NSJSONReadingOptions()) as! NSArray
                }catch let error as NSError{
                    print(error.localizedDescription)
                }
                for _ in 1...100{
                        NSLog("main thread? %@", NSThread.isMainThread() ? "YES" : "NO");
                }
            }
            self.completeOperation()
        }
    }

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

This is the ConcurrentOperation class. I copied it from the post in the link above.

class ConcurrentOperation : NSOperation {

    override var asynchronous: Bool {
        return true
    }

    override var concurrent: 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()
    }
}

When I execute this code, it keeps saying that it runs on the main thread: 2016-08-12 18:25:45.799 Stamp Catalague Preloader[1807:31357] main thread? YES.

How is this happening? Thanks for your help;

Upvotes: 1

Views: 756

Answers (1)

Rob
Rob

Reputation: 438232

The issue is that Alamofire dispatches its completion handlers to the main queue (the theory being that they want to make it easy for you to update the UI and/or model).

If you want to use a different queue for the completion handlers, supply the queue parameter to the response or responseJSON methods.

For example, have some property:

var completionHandlerQueue = dispatch_queue_create("com.domain.completionhandler", nil)

And then, when you perform the request:

Alamofire.request(.GET, URLString).responseJSON(queue: completionHandlerQueue) { response in
    guard let test = response.result.value else {
        print("no data")
        print(response.result.error)
        return
    }

    // do something with `test` here
    print(test)

    NSLog("main thread? %@", NSThread.isMainThread() ? "YES" : "NO");
}

This raises the question of whether you really care if the completion handlers are called on the main queue or not. Sure, if you're doing something computationally intensive there, then, by all means, specify a different queue. But there's no reason why an operation can't perform some of its tasks on the main queue. In fact, it's awfully useful at times to just have the completion handlers run on the main queue, saving you from needing to synchronize your model updates and/or dispatch your UI updates to the main queue.

To step back even further, we have to ask what the intent of using NSOperation is. If the goal is merely to enjoy asynchronous processing of requests, NSOperation is not needed. But if (a) you're using NSOperation to manage dependencies of different operations; and (b) you're not using background sessions, then, by all means, wrap your requests in operations.

Upvotes: 1

Related Questions