AlamoFire 프레임 워크를 사용한 후, CompletionHandler가 메인 스레드에서 실행되는 것을 확인했습니다. 아래 코드가 완료 핸들러 내에서 Core Data 가져 오기 작업을 만드는 데 좋은 방법인지 궁금합니다.
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);
}
});
}
답변
이것은 정말 좋은 질문입니다. 귀하의 접근 방식은 완벽하게 유효합니다. 그러나 Alamofire는 실제로이를 더욱 간소화하는 데 도움이 될 수 있습니다.
예제 코드 디스패치 대기열 분석
예제 코드에서 다음 디스패치 큐 사이를 점프합니다.
- NSURLSession 디스패치 큐
- 유효성 검사 및 직렬 변환기 처리를위한 TaskDelegate 디스패치 큐
- 완료 핸들러를 호출하기위한 기본 디스패치 큐
- JSON 처리를위한 높은 우선 순위 대기열
- 사용자 인터페이스를 업데이트하기위한 기본 디스패치 대기열 (필요한 경우)
보시다시피, 당신은 여기 저기 뛰어 다니고 있습니다. Alamofire 내부의 강력한 기능을 활용하는 대체 접근 방식을 살펴 보겠습니다.
Alamofire 응답 디스패치 대기열
Alamofire는 자체 저수준 처리에 최적의 접근 방식이 내장되어 있습니다. response
궁극적으로 모든 사용자 지정 응답 serializer에 의해 호출되는 단일 메서드는 사용하기로 선택한 경우 사용자 지정 디스패치 큐를 지원합니다.
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가 실제로 작동하는 방식의 내부에 익숙해지는 것이 얼마나 중요한지 충분히 강조 할 수 없습니다. 자신의 코드를 개선하는 데 실제로 도움이되는 무언가를 언제 찾을 수 있는지 결코 알 수 없습니다.
답변
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
}
})
답변
@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)
}
}
}
}