1. ホーム
  2. swift

[解決済み] AlamoFireのJSONリクエスト用非同期completionHandler

2023-07-22 03:04:54

質問

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は、実際にこれをさらに合理化するのに役立ちます。

コード例 ディスパッチキュー内訳

サンプルコードでは、以下のディスパッチキューを行き来しています。

  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が実際にどのように動作するかの内部構造に精通することがいかに重要であるかは、いくら強調してもしきれません。いつ自分のコードを改善するのに役立つ何かを見つけることができるかわかりません。