6

Background Fetch を使用してバックグラウンドでコンテンツを取得するアプリがあります。

したがって、Background Fetch が発生する場合は、私のapplication:performFetchWithCompletionHandler:メソッドが呼び出されます。このメソッドでは、NSURLConnection を使用してコンテンツを非同期でフェッチします。

私の現在の実装では、リクエストを開始してからcompletionHandlerwith を呼び出すだけUIBackgroundFetchResultNewDataです。私はこれが正しくないことを知っています。completionHandlerだから私の質問は、メソッドで非同期リクエストが終了したときに、どうすれば正しく呼び出すことができるかということですconnection:didReceiveData:

4

1 に答える 1

15

その通りです。完了ハンドラーは、フェッチが実際に完了したときにのみ呼び出す必要があります。UIBackgroundFetchResultNewDataそうしないと、接続が完了する前に iOS がアプリケーションをスリープ状態に戻してしまう可能性があり、アプリケーションはそれまでに対UIBackgroundFetchResultNoDataまたはそれまで判断できないはずUIBackgroundFetchResultFailedです。接続が成功することをどのように確認しますか?

completionHandler を保持し、接続が終了したときにのみ呼び出す必要があります。ほとんどの場合、インスタンス変数をデリゲートに追加し、そこに完了ハンドラーをコピーして、完了したらそれを呼び出します。

余談:connection:didReceiveData:リクエストの終了を通知しません。ドキュメントごと:

接続として送信され、データが段階的に読み込まれます。

[...]

デリゲートは、配信された各データ オブジェクトのコンテンツを連結して、URL ロード用の完全なデータを構築する必要があります。

URL 接続の最終的な結果は、呼び出しの数に制限はありません。

編集: 正しいタイプのインスタンス変数を作成し、ブロックをそれにコピーすることで、ブロックを保存します。他の種類の Objective-C オブジェクトとは異なり、ブロックは最初にスタック上に作成されるため、ブロックには通常とは異なるセマンティクスがあります。正味の効果は、あなたが常にcopyそれらを持っているということです。コピー時にスタック上にある場合は、最終的にヒープになります。それらがすでにヒープ上にある場合、ブロックはとにかく常に不変であるため、コピーは単に保持として機能します。

そう:

@implementation XXMDYourClass
{
    // syntax follow the C rule; read from the centre outwards
    void (^_completionHandler)(UIBackgroundFetchResult);
}

- (id)initWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    self = [super init];

    if(self)
    {
        // keep the block by copying it; release later if
        // you're not using ARC
        _completionHandler = [completionHandler copy];
    }

    return self;
}

- (void)somethingThatHappensMuchLater
{
     _completionHandler(UIBackgroundFetchResultWhatever);
}

@end
于 2013-10-16T21:57:59.353 に答える