2

I'm using Alamofire (AF) in a concurrent operation queue to run network commands in my project. Sometimes AF's completionHandler doesn't fire, leaving my NSOperation hanging (waiting for a finish message that it will never receive).

Eg. I'll see the "response" log, but no corresponding "see me" log from AF's dispatch_async below:

public func response(priority: Int = DISPATCH_QUEUE_PRIORITY_DEFAULT, queue: dispatch_queue_t? = nil, serializer: (NSURLRequest, NSHTTPURLResponse?, NSData?, NSError?) -> (AnyObject?, NSError?), completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
NSLog("markse-response")

    dispatch_async(self.delegate.queue, {
NSLog("markse-see me")
        dispatch_async(dispatch_get_global_queue(priority, 0), {
            if var error = self.delegate.error {
                dispatch_async(queue ?? dispatch_get_main_queue(), {
                    completionHandler(self.request, self.response, nil, error)
                })
            } else {
                let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data, nil)

                dispatch_async(queue ?? dispatch_get_main_queue(), {
                    completionHandler(self.request, self.response, responseObject, serializationError)
                })
            }
        })
    })

    return self
}

This is my NSOperation (AsynchronousCommand is an NSOperation subclass):

import Alamofire

class SettingsListCommand: AsynchronousCommand {

    override func execute() {
        if cancelled { return }

        let endpoint = "https://api.github.com/users/cog404/orgs"

        DLogVerbose("AF request")
        weak var weakSelf = self
        Alamofire.request(.GET,
            endpoint,
            parameters:nil)
            .responseJSON {(request, response, JSON, error) in
                DLogVerbose("AF response")
                if let strongSelf = weakSelf {
                    if strongSelf.cancelled {
                        strongSelf.finish()
                        return
                    }
                    DLogVerbose(JSON)
                    strongSelf.finish()
                }
        }
    }

}

This only happens 'occasionally', making this very difficult to debug.

Does anyone with a good understanding of threading know what could be going wrong?

Any advice very much appreciated. A project to help illustrate the problem is here.

mark.siedle
  • 78
  • 2
  • 6

1 Answers1

3

The request delegate's dispatch queue is serial, meaning that it will only process a single block at a time, in order of when the blocks were dispatched (FIFO). If the second log statement isn't firing, it's because the previous block didn't yet finish.

mattt
  • 19,450
  • 7
  • 69
  • 83
  • 2
    My 2 cents: this solution feels overly complicated. If you _really_ need `NSOperation`, just use a block operation and `dispatch_semaphore`. – mattt Sep 24 '14 at 23:33
  • 2
    Thanks mattt. It took me a while to read up on GCD, but that behaviour makes sense now. I'll consider the dispatch_semaphore approach after more reading (I've lived in the NSOperation world for a long time). – mark.siedle Oct 02 '14 at 03:36