sloeberGJ sloeberGJ - 3 months ago 40
Swift Question

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;

Rob Rob
Answer

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.

Comments