AlamoFire用于JSON请求的异步completionHandler

时间:2015-04-24 16:10:35

标签: swift alamofire

使用AlamoFire框架后,我注意到completionHandler在主线程上运行。我想知道下面的代码是否是在完成处理程序中创建核心数据导入任务的好习惯:

Alamofire.request(.GET, "http://myWebSite.com", parameters: parameters)
            .responseJSON(options: .MutableContainers) { (_, _, JSON, error) -> Void in
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
                    if let err = error{
                        println("Error:\(error)")
                        return;
                    }

                    if let jsonArray = JSON as? [NSArray]{                       
                        let importer = CDImporter(incomingArray: jsonArray entity: "Artist", map: artistEntityMap);

                    }
                });
            }

3 个答案:

答案 0 :(得分:150)

这是一个非常好的问题。你的方法是完全有效的。但是,Alamofire实际上可以帮助您更精简这一点。

您的示例代码调度队列细分

在您的示例代码中,您将在以下调度队列之间跳转:

  1. NSURLSession调度队列
  2. 用于验证和序列化处理的TaskDelegate调度队列
  3. 调用完成处理程序的主调度队列
  4. JSON处理的高优先级队列
  5. 用于更新用户界面的主调度队列(如有必要)
  6. 正如你所看到的,你到处都是跳来跳去的。让我们来看看利用Alamofire内部强大功能的另一种方法。

    Alamofire响应调度队列

    Alamofire拥有内置于其自身低级处理中的最佳方法。如果您选择使用自定义响应序列化程序,则最终由所有自定义响应序列化程序调用的单response方法支持自定义分派队列。

    虽然GCD在调度队列之间跳跃是惊人的,但您希望避免跳转到忙碌的队列(例如主线程)。通过在异步处理过程中消除跳回主线程,您可以大大加快速度。以下示例演示了如何使用Alamofire逻辑直接开箱即用。

    Alamofire 1.x

    let queue = dispatch_queue_create("com.cnoon.manager-response-queue", DISPATCH_QUEUE_CONCURRENT)
    
    let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
    request.response(
        queue: queue,
        serializer: Request.JSONResponseSerializer(options: .AllowFragments),
        completionHandler: { _, _, JSON, _ in
    
            // You are now running on the concurrent `queue` you created earlier.
            println("Parsing JSON on thread: \(NSThread.currentThread()) is main thread: \(NSThread.isMainThread())")
    
            // Validate your JSON response and convert into model objects if necessary
            println(JSON)
    
            // To update anything on the main thread, just jump back on like so.
            dispatch_async(dispatch_get_main_queue()) {
                println("Am I back on the main thread: \(NSThread.isMainThread())")
            }
        }
    )
    

    Alamofire 3.x(Swift 2.2和2.3)

    let queue = dispatch_queue_create("com.cnoon.manager-response-queue", DISPATCH_QUEUE_CONCURRENT)
    
    let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
    request.response(
        queue: queue,
        responseSerializer: Request.JSONResponseSerializer(options: .AllowFragments),
        completionHandler: { response in
            // You are now running on the concurrent `queue` you created earlier.
            print("Parsing JSON on thread: \(NSThread.currentThread()) is main thread: \(NSThread.isMainThread())")
    
            // Validate your JSON response and convert into model objects if necessary
            print(response.result.value)
    
            // To update anything on the main thread, just jump back on like so.
            dispatch_async(dispatch_get_main_queue()) {
                print("Am I back on the main thread: \(NSThread.isMainThread())")
            }
        }
    )
    

    Alamofire 4.x(Swift 3)

    let queue = DispatchQueue(label: "com.cnoon.response-queue", qos: .utility, attributes: [.concurrent])
    
    Alamofire.request("http://httpbin.org/get", parameters: ["foo": "bar"])
        .response(
            queue: queue,
            responseSerializer: DataRequest.jsonResponseSerializer(),
            completionHandler: { response in
                // You are now running on the concurrent `queue` you created earlier.
                print("Parsing JSON on thread: \(Thread.current) is main thread: \(Thread.isMainThread)")
    
                // Validate your JSON response and convert into model objects if necessary
                print(response.result.value)
    
                // To update anything on the main thread, just jump back on like so.
                DispatchQueue.main.async {
                    print("Am I back on the main thread: \(Thread.isMainThread)")
                }
            }
        )
    

    Alamofire Dispatch Queue Breakdown

    以下是此方法所涉及的不同调度队列的细分。

    1. NSURLSession调度队列
    2. 用于验证和序列化处理的TaskDelegate调度队列
    3. 用于JSON处理的自定义管理器并发调度队列
    4. 用于更新用户界面的主调度队列(如有必要)
    5. 摘要

      通过消除返回主调度队列的第一跳,您已消除了潜在的瓶颈,并且您已使整个请求和处理异步。真棒!

      话虽如此,我不能强调要熟悉Alamofire真正运作的内部是多么重要。你永远不知道什么时候你可以找到真正能帮助你改进自己代码的东西。

答案 1 :(得分:2)

Swift 3.0的小更新,Alamofire(4.0.1),编辑@cnoon回答:

let queue = DispatchQueue(label: "com.cnoon.manager-response-queue",
                          qos: .userInitiated,
                          attributes:.concurrent)
Alamofire?.request(SERVER_URL, method: .post,
parameters: ["foo": "bar"], 
encoding: JSONEncoding.default,//by default
headers: ["Content-Type":"application/json; charset=UTF-8"])
.validate(statusCode: 200..<300).//by default
responseJSON(queue: queue, options: .allowFragments, 
completionHandler: { (response:DataResponse<Any>) in

        switch(response.result) {
        case .success(_):
            break
        case .failure(_):
            print(response.result.error)
            if response.result.error?._code == NSURLErrorTimedOut{
                //TODO: Show Alert view on netwok connection.
            }
            break
        }
    })

答案 2 :(得分:1)

只是补充来自@cnoon的完美答案,如果你喜欢我使用ResponseObjectSerializable,你可以在请求扩展本身上嵌入这个并发行为:

extension Request {
    public func responseObject<T: ResponseObjectSerializable>(completionHandler: Response<T, NSError> -> Void) -> Self {
        let responseSerializer = ResponseSerializer<T, NSError> { request, response, data, error in
            guard error == nil else { return .Failure(error!) }

            let JSONResponseSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
            let result = JSONResponseSerializer.serializeResponse(request, response, data, error)

            switch result {
            case .Success(let value):
                if let
                    response = response,
                    responseObject = T(response: response, representation: value)
                {
                    return .Success(responseObject)
                } else {
                    let failureReason = "JSON could not be serialized into response object: \(value)"
                    let error = Error.errorWithCode(.JSONSerializationFailed, failureReason: failureReason)
                    return .Failure(error)
                }
            case .Failure(let error):
                return .Failure(error)
            }
        }

        let queue = dispatch_queue_create("my.queue", DISPATCH_QUEUE_CONCURRENT)
        return response(queue: queue, responseSerializer: responseSerializer) { response in
            dispatch_async(dispatch_get_main_queue()) {
                completionHandler(response)
            }
        }
    }
}