2

サーバー上のデータベースからJSON形式のデータを返すWebサービスを使用しています。このデータは、iPhoneアプリの一部のビューに表示されます。

アプリは無料なので、DBに何千もの行があり、もちろんJSONデータにもダウンロードする必要があります。

JSONデータの遅延読み込みを行うにはどうすればよいですか?コード行をいただければ幸いです。

ありがとう。

編集1:

つまり、「テーブルからすべてを選択」などのSQLリクエストを使用して、このデータをJSONとしてアプリに返します。

どうすれば小さな番号をリクエストしてから他の番号を追加できますか?

4

1 に答える 1

3

「遅延読み込み」という用語は、単一のJSON応答に完全に適しているとは限らず、あるアプローチを別のアプローチよりも優先する可能性があります。それでは、非常に大規模なサーバーデータベースにもかかわらず、ユーザーエクスペリエンスの向上を実現したいという根本的な質問に取り組みましょう。

  1. JSONが非常に大きい場合は、最初のxレコードをダウンロードしてから、サーバーデータの次の「ページ」に対して後続のリクエストを行う「ページング」メタファーをいつでも検討できます。おそらくリクエストの間にサーバーレコードを追加できることを考えると、その実装について慎重に検討する必要がありますが、おそらく簡単に実行できます。

  2. JSONをより効率的にする別の方法は、最初のリクエストで返されるデータを制限することです。たとえば、最初のリクエストは、レコードのすべてまたは妥当なサブセットの識別子またはヘッダー情報のみを返すことができ、その後、追加の詳細を求めるリクエストを行うことができます(たとえば、ユーザーが詳細画面に移動します)。

  3. 前のポイントの特定の順列は、JSONに現在大きなデータ要素(たとえば、画像やサウンドファイルなどのBase64でエンコードされたバイナリデータ)が含まれている場合です。それらを最初の応答から取り除くことによって、それは多くの問題に対処します。また、データ自体ではなく、このバイナリデータのURLを返すことができれば、遅延読み込みに間違いなく役立ちます(また、33%大きいBase64エンコーディングではなくバイナリ情報を簡単にダウンロードできるようになります) 。また、画像を扱っている場合は、サムネイルと大きな画像のアイデアを検討し、特に遅延読み込みモデルで後者を処理することもできます。

  4. ストリーミングをサポートするバージョンのXML解析の実装を検討できます。標準のNSXMLParser実装では、解析を続行する前に、XMLフィード全体(または可能な限り)を一度にメモリにロードしようとします(メソッド名が反対を示唆している場合でも)。アプリを使用している場合LibXML2(AppleのAdvancedURLConnectionsサンプルなど)、初期データがユーザーに表示されている間、バックグラウンドでXMLのダウンロードと解析を続行できます。これにより、ほとんどの人が「遅延読み込み」(つまり、結果をユーザーに表示する前にすべてを待つ必要がない)と「熱心な読み込み」(つまり、次のレコードをダウンロードして次のレコードをダウンロードする)に関連するメリットが同時に得られます。ユーザーがそこに行くとき、すでに準備ができています)。

よりインテリジェントな提案を行うには、JSONの性質とその背後にあるデータに関する詳細情報を共有したり、「遅延読み込み」が解決策であると考える理由を説明したりする必要があります。データの分析を行うまでの特定のソリューション(たとえば、数千行のJSONは、単一の大きな画像よりも小さい場合があります)。


アップデート:

最初のアプローチを採用する場合は、最初にWebサービスを変更して、要求に応じて、特定のレコード番号から始まるnレコードのみを配信するようにする必要があります。また、サーバー上のレコードの総数を返す必要があるかもしれません。そうすれば、スクロールする量についてユーザーに視覚的な手がかりを与えることができます。

次に、このモデルをサポートするようにiOSアプリを更新する必要があります。したがって、UIで結果をスクロールしているときに、ユーザーインターフェイスがスクロールイベントに応答して、次のいずれかを表示するようにします。(a)既に取得している場合は、実際の結果。または(b)問題のレコードが取得されていることを視覚的に示し、サーバーからの情報を非同期的に要求するUI。でこれを行っている場合は、次のUITableViewCellようになります。

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self initiateRequestForFirstSetOfData];
}

- (void)initiateRequestForFirstSetOfData
{
    // asynchronously retrieve the data from the server:
    //   (a) retrieve the total number of records
    //   (b) retrieve the actual data for the first n records
    // and when this is done, dispatch a `[self.tableView reloadData]` back to the
    // main queue.
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.totalNumberOfRecordsOnServer;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    BOOL isAvailable = ... // do whatever logic to determine if this row has already been retrieved

    if (isAvailable)
    {
        // configure the cell like normal here
    }
    else
    {
        // configure the cell to indicate that a fetch is in progress here.

        // perhaps add a UIActivityIndicatorView and startAnimating it

        dispatch_async(backgroundQueue, ^{

            // initiate the request for the data (if you haven't already)

            dispatch_async(dispatch_get_main_queue(), ^{

                // don't just update the UI here, but make sure the cell
                // in question is still on screen by calling `UITableView`
                // method `cellForRowAtIndexPath`, not to be confused with
                // this method, which is a `UITableViewController` method.

                UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                if (cell)
                {
                    // update the cell: sometimes you can get away with
                    // updating the cell directly, sometimes you want to
                    // just call something like:
                    //
                    // [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
                }
            });
        });
    }

    cell.imageView.contentMode = UIViewContentModeScaleAspectFill;

    return cell;
}

上記のかなり単純なコードに対して私が提案するかもしれないたくさんの改良がありますが、うまくいけば、それはあなたに基本的な考えを与えるでしょう。(a)データの合計行数を把握します。(b)最初のnレコードを取得します。(c)ユーザーがレコードにスクロールすると、レコードを持っている場合はそれを表示しますが、持っていない場合は、非同期でそのデータを取得することを視覚的に示します。(d)データが到着したら、必要に応じてUIを更新します。

私が言ったように、私はおそらく上記のコードに対していくつかの改良を追求するでしょう、例えば、私はおそらく非同期検索をビューコントローラー自体に埋め込むのではなく、モデルにそれを行い、UIを更新するためのデリゲートパターンを持っています、私はおそらくディスパッチキューではなくオペレーションキューを使用するので、不要になったリクエストなどをキャンセルできますが、基本的な考え方は理解できます。

を使用している場合はUICollectionViewController、上記のコードに類似しています。スクロールビューを使用している場合、パターンは非常に似ていますが、代わりにUIScrollViewDelegateメソッドに応答しscrollViewDidScrollます(上記のようなコードだけでなく、UIKitスクロールした要素を解放するコードも記述する必要があります)。画面外にも)。

于 2012-12-20T23:18:27.070 に答える