AlamoFire asynchronous completeHandler cho yêu cầu JSON


80

Sau khi sử dụng khuôn khổ AlamoFire, tôi nhận thấy rằng completeHandler được chạy trên luồng chính. Tôi tự hỏi liệu đoạn mã dưới đây có phải là một phương pháp hay để tạo tác vụ nhập Dữ liệu cốt lõi trong trình xử lý hoàn thành hay không:

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

                    }
                });
            }

Câu trả lời:


156

Đây thực sự là một câu hỏi tốt. Cách tiếp cận của bạn là hoàn toàn hợp lệ. Tuy nhiên, Alamofire thực sự có thể giúp bạn hợp lý hóa việc này hơn nữa.

Phân tích hàng đợi công văn mã mẫu của bạn

Trong mã ví dụ của bạn, bạn đang nhảy giữa các hàng đợi gửi sau:

  1. Hàng đợi gửi NSURLSession
  2. Hàng đợi gửi TaskDelegate để xác thực và xử lý bộ tuần tự
  3. Hàng đợi công văn chính để gọi trình xử lý hoàn thành của bạn
  4. Hàng đợi ưu tiên cao để xử lý JSON
  5. Hàng đợi công văn chính để cập nhật giao diện người dùng (nếu cần)

Như bạn có thể thấy, bạn đang nhảy khắp nơi. Chúng ta hãy xem xét một phương pháp thay thế tận dụng một tính năng mạnh mẽ bên trong Alamofire.

Hàng đợi Công văn Phản hồi Alamofire

Alamofire có một cách tiếp cận tối ưu được tích hợp trong quá trình xử lý cấp thấp của riêng nó. responsePhương thức duy nhất cuối cùng được gọi bởi tất cả các trình tuần tự phản hồi tùy chỉnh có hỗ trợ cho hàng đợi điều phối tùy chỉnh nếu bạn chọn sử dụng nó.

Mặc dù GCD rất tuyệt vời trong việc nhảy giữa các hàng đợi gửi, nhưng bạn muốn tránh chuyển sang một hàng đang bận (ví dụ: luồng chính). Bằng cách loại bỏ bước quay trở lại luồng chính ở giữa quá trình xử lý không đồng bộ, bạn có thể tăng tốc mọi thứ lên đáng kể. Ví dụ sau đây trình bày cách thực hiện điều này bằng cách sử dụng logic Alamofire thẳng ra khỏi hộp.

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 và 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)")
            }
        }
    )

Phân tích hàng đợi công văn Alamofire

Dưới đây là bảng phân tích các hàng đợi gửi khác nhau liên quan đến phương pháp này.

  1. Hàng đợi gửi NSURLSession
  2. Hàng đợi gửi TaskDelegate để xác thực và xử lý bộ tuần tự
  3. Hàng đợi gửi đồng thời của trình quản lý tùy chỉnh để xử lý JSON
  4. Hàng đợi công văn chính để cập nhật giao diện người dùng (nếu cần)

Tóm lược

Bằng cách loại bỏ bước đầu tiên quay trở lại hàng đợi gửi chính, bạn đã loại bỏ một nút thắt cổ chai tiềm ẩn cũng như bạn đã thực hiện toàn bộ yêu cầu và xử lý không đồng bộ của mình. Tuyệt vời!

Với điều đó đã nói, tôi không thể nhấn mạnh tầm quan trọng của việc làm quen với nội bộ về cách thức hoạt động thực sự của Alamofire. Bạn không bao giờ biết khi nào bạn có thể tìm thấy thứ gì đó thực sự có thể giúp bạn cải thiện mã của chính mình.


3
Cảm ơn vì lời giải thích cặn kẽ, @cnoon. Có vẻ như tham số thứ hai cho responsephương thức bây giờ được gọi responseSerializerchứ không phải serializer(trong Alamofire 3.0). Điều đó gây ra một Cannot call value of non-function type 'NSHTTPURLResponse?'lỗi khiến tôi bối rối một chút.
Hélène Martin

Vui lòng tải lên các thay đổi, mã không hoạt động được. Swift 2.1, XCode 7.1
Beraliv

Còn về responseJSON? Làm cách nào tôi có thể chuyển vào tham số hàng đợi
OMGPOP

@cnoon, có thể rất hay nếu bạn thêm bản cập nhật cho swift 3 nữa.
Mike.R

Hiện cũng hoạt động cho Swift 3. Brilliant
dejavu89

2

Bản cập nhật nhỏ cho Swift 3.0, Alamofire (4.0.1), Chỉnh sửa cho câu trả lời @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
        }
    })

1

Chỉ cần bổ sung câu trả lời hoàn hảo từ @cnoon, nếu bạn thích tôi đang sử dụng, ResponseObjectSerializablebạn có thể nhúng hành vi đồng thời này vào chính tiện ích mở rộng yêu cầu:

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)
            }
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.