Используя структуру AlamoFire, я заметил, что функция завершенияHandler запускается в основном потоке. Мне интересно, является ли приведенный ниже код хорошей практикой для создания задачи импорта основных данных в обработчике завершения:
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);
}
});
}
Ответ 1
Это действительно хороший вопрос. Ваш подход совершенно верен. Тем не менее, Alamofire действительно может помочь вам оптимизировать это еще больше.
Отказ от очереди отправки кода кода
В вашем примере кода вы прыгаете между очередями отправки:
- Очередь отправки NSURLSession
- TaskDelegate диспетчерская очередь для проверки и обработки сериализатора
- Основная очередь отправки для вызова обработчика завершения.
- Очередь с высоким приоритетом для обработки JSON
- Основная очередь отправки для обновления пользовательского интерфейса (при необходимости)
Как вы можете видеть, вы прыгаете повсюду. Позвольте взглянуть на альтернативный подход, используя мощную функцию внутри 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
Вот разбивка различных очередей отправки, связанных с этим подходом.
- Очередь отправки NSURLSession
- TaskDelegate диспетчерская очередь для проверки и обработки сериализатора
- Пользовательская диспетчерская очередь для диспетчеризации для обработки JSON
- Основная очередь отправки для обновления пользовательского интерфейса (при необходимости)
Резюме
Исключив первый переход обратно в основную очередь отправки, вы устранили потенциальное узкое место, а также сделали свой весь запрос и обработали асинхронно. Отлично!
С учетом сказанного я не могу достаточно подчеркнуть, насколько важно познакомиться с внутренними аспектами того, как Alamofire действительно работает. Вы никогда не знаете, когда найдете что-то, что действительно поможет вам улучшить свой собственный код.
Ответ 2
Небольшое обновление для Swift 3.0, Alamofire (4.0.1), Edit для ответа @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)
}
}
}
}