Controlador de terminación asíncrono AlamoFire para solicitud JSON
Después de haber usado el framework AlamoFire he notado que el controlador completionHandler se ejecuta en el hilo principal. Me pregunto si el siguiente código es una buena práctica para crear una tarea de importación de datos básicos dentro del controlador de finalización:
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 answers
Esta es una muy buena pregunta. Su enfoque es perfectamente válido. Sin embargo, Alamofire puede ayudarte a optimizar esto aún más.
Su Desglose de La Cola de Envío de Código de Ejemplo
En su código de ejemplo, está saltando entre las siguientes colas de despacho:
- Cola de despacho NSURLSession
- TaskDelegate la cola de despacho para la validación y el procesamiento del serializador
- Cola de despacho principal para llamar a su controlador de finalización
- Alta cola de prioridad para el manejo de JSON
- Cola de despacho principal para actualizar la interfaz de usuario (si es necesario)
Como puedes ver, estás saltando por todas partes. Echemos un vistazo a un enfoque alternativo aprovechando una poderosa característica dentro de Alamofire.
Colas de Envío de Respuesta de Alamofire
Alamofire tiene un enfoque óptimo integrado en su propio procesamiento de bajo nivel. El único método response
que en última instancia es llamado por todos los serializadores de respuesta personalizada tiene soporte para una cola de despacho personalizada si elige usarla.
Si bien GCD es increíble para saltar entre colas de despacho, debe evitar saltar a una cola que esté ocupada (por ejemplo, el hilo principal). Al eliminar el salto al hilo principal en medio del procesamiento asíncrono, puede acelerar las cosas considerablemente. El siguiente ejemplo muestra cómo hacer esto usando Alamofire logic directamente fuera de la caja.
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 y 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)")
}
}
)
Desglose de la Cola de Despacho de Alamofire
Aquí está el desglose de las diferentes colas de despacho involucradas con este enfoque.
- Cola de despacho NSURLSession
- TaskDelegate la cola de despacho para la validación y el procesamiento del serializador
- Custom manager concurrent dispatch queue for JSON handling
- Cola de despacho principal para actualizar la interfaz de usuario (si necesario)
Resumen
Al eliminar el primer salto de vuelta a la cola de despacho principal, ha eliminado un cuello de botella potencial, así como ha hecho que toda su solicitud y el procesamiento sea asincrónico. ¡Órale!
Dicho esto, no puedo enfatizar lo suficiente lo importante que es familiarizarse con los aspectos internos de cómo Alamofire realmente funciona. Nunca se sabe cuando se puede encontrar algo que realmente puede ayudar a mejorar su propio código.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-02 18:24:09
Pequeña actualización para Swift 3.0, Alamofire (4.0.1), Editar para @cnoon respuesta:
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
}
})
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-02 12:05:49
Simplemente complementando la respuesta perfecta de @cnoon, si te gusta me está usando ResponseObjectSerializable
puedes incrustar este comportamiento concurrente en la propia extensión de la solicitud:
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)
}
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-27 01:12:39