【问题标题】:AlamoFire asynchronous completionHandler for JSON request用于 JSON 请求的 AlamoFire 异步完成处理程序
【发布时间】:2015-07-03 08:23:26
【问题描述】:

使用 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);

                    }
                });
            }

【问题讨论】:

    标签: swift alamofire


    【解决方案1】:

    这是一个非常好的问题。你的方法是完全有效的。但是,Alamofire 实际上可以帮助您进一步简化这一过程。

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

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

    1. NSURLSession 调度队列
    2. 用于验证和序列化程序处理的 TaskDelegate 调度队列
    3. 用于调用完成处理程序的主调度队列
    4. JSON 处理的高优先级队列
    5. 用于更新用户界面的主调度队列(如有必要)

    如您所见,您到处乱窜。让我们看一下利用 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 调度队列细分

    这里是这种方法所涉及的不同调度队列的细分。

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

    总结

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

    话虽如此,我不能再强调熟悉 Alamofire 的内部工作原理的重要性了。你永远不知道什么时候能找到真正能帮助你改进自己代码的东西。

    【讨论】:

    • 感谢@cnoon 的详尽解释。看来response 方法的第二个参数现在称为responseSerializer 而不是serializer(在Alamofire 3.0 中)。这导致了一个Cannot call value of non-function type 'NSHTTPURLResponse?' 错误,让我有点困惑。
    • 请上传更改,代码不可用。斯威夫特 2.1,XCode 7.1
    • responseJSON 怎么样?如何传入队列参数
    • @cnoon ,如果你也为 swift 3 添加更新会很好。
    • 现在也适用于 Swift 3。辉煌
    【解决方案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
            }
        })
    

    【讨论】:

      【解决方案3】:

      只是补充了@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)
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-18
        • 1970-01-01
        • 1970-01-01
        • 2016-06-02
        • 2015-12-17
        相关资源
        最近更新 更多