37

私はすべてのネットワーク操作にAFNetworkingandを使用しています。SDURLCache

私はSDURLCacheこのように設定しました:

SDURLCache *urlCache = [[SDURLCache alloc]
        initWithMemoryCapacity:1024*1024*2   // 2MB mem cache
        diskCapacity:1024*1024*15 // 15MB disk cache
        diskPath:[SDURLCache defaultCachePath]];
    [urlCache setMinCacheInterval:1];
    [NSURLCache setSharedURLCache:urlCache];

私のリクエストはすべて cachePolicy を使用していますNSURLRequestUseProtocolCachePolicy。これは、アップルのドキュメントによると次のように機能します。

要求に対する NSCachedURLResponse が存在しない場合、データは元のソースからフェッチされます。要求に対するキャッシュされた応答がある場合、URL 読み込みシステムは応答をチェックして、コンテンツの再検証が必要であると指定されているかどうかを判断します。内容を再検証する必要がある場合は、元のソースに接続して、内容が変更されているかどうかを確認します。変更されていない場合は、ローカル キャッシュから応答が返されます。変更されている場合、データは元のソースからフェッチされます。

コンテンツを再検証する必要があることがキャッシュされた応答で指定されていない場合、応答で指定された最大経過時間または有効期限が調べられます。キャッシュされた応答が十分に新しい場合、応答はローカル キャッシュから返されます。応答が古くなっていると判断された場合は、元のソースで新しいデータがチェックされます。新しいデータが利用可能な場合、データは元のソースからフェッチされます。それ以外の場合は、キャッシュから返されます。

したがって、キャッシュが古くない限り、機内モードでもすべてが完全に機能します。キャッシュの有効期限が切れると (max-age など)、失敗ブロックが呼び出されます。

内部を少し掘り下げていてSDURLCache、このメソッドは有効なデータを含む応答を返します(データを文字列に解析し、キャッシュされた情報が含まれています)

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    request = [SDURLCache canonicalRequestForRequest:request];

    NSCachedURLResponse *memoryResponse =
        [super cachedResponseForRequest:request];
    if (memoryResponse) {
        return memoryResponse;
    }

    NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL];

    // NOTE: We don't handle expiration here as even staled cache data is
    // necessary for NSURLConnection to handle cache revalidation.
    // Staled cache data is also needed for cachePolicies which force the
    // use of the cache.
    __block NSCachedURLResponse *response = nil;
    dispatch_sync(get_disk_cache_queue(), ^{
        NSMutableDictionary *accesses = [self.diskCacheInfo
            objectForKey:kAFURLCacheInfoAccessesKey];
        // OPTI: Check for cache-hit in in-memory dictionary before to hit FS
        if ([accesses objectForKey:cacheKey]) {
            response = [NSKeyedUnarchiver unarchiveObjectWithFile:
                [_diskCachePath stringByAppendingPathComponent:cacheKey]];
            if (response) {
                // OPTI: Log entry last access time for LRU cache eviction
                // algorithm but don't save the dictionary
                // on disk now in order to save IO and time
                [accesses setObject:[NSDate date] forKey:cacheKey];
                _diskCacheInfoDirty = YES;
            }
        }
    });

    // OPTI: Store the response to memory cache for potential future requests
    if (response) {
        [super storeCachedResponse:response forRequest:request];
    }

    return response;
}

したがって、この時点で何をすべきかわかりません。応答は OS によって処理され、その後AFNetworking

- (void)connection:(NSURLConnection *)__unused connection 
  didFailWithError:(NSError *)error

内部AFURLConnectionOperation

4

3 に答える 3

12

さて、私は最終的にそれほど醜くない回避策に到達しました:

初め

IOS5/IOS6 を使用している場合は、SDURLCache をドロップしてネイティブのものを使用できます。

//Set Cache
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                     diskCapacity:20 * 1024 * 1024
                                                         diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];

ただし、IOS5 では https 要求は IOS6 ではキャッシュされないことに注意してください。

2番

Prefix.pchAFNetworking がインターネット接続の監視を開始できるように、次のフレームワークを追加する必要があります。

#import <MobileCoreServices/MobileCoreServices.h>
#import <SystemConfiguration/SystemConfiguration.h>

三番

AFHTTPClient インスタンスが必要なので、すべての送信リクエストをインターセプトして変更することができますcachePolicy

-(NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {

    NSMutableURLRequest * request = [super requestWithMethod:method path:path parameters:parameters];
    if (request.cachePolicy == NSURLRequestUseProtocolCachePolicy && self.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
        request.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
    }

    if (self.networkReachabilityStatus == AFNetworkReachabilityStatusUnknown) {

        puts("uknown reachability status");
    }

    return request;
}

これらのコードの平和により、wifi/3g が利用できないときを検出し、何があっても常にキャッシュを使用する要求を指定できるようになりました。(オフラインモード)

ノート

  • これは、アプリケーションが開始されるとすぐにリクエストが行われ、AF がまだインターネット ステータスを取得していない場合に発生する可能性がnetworkReachabilityStatusあります。AFNetworkReachabilityStatusUnknown

  • これが機能するためには、サーバーが http 応答で正しいキャッシュ ヘッダーを設定する必要があることに注意してください。

アップデート

IOS6 では、インターネットがない状況でキャッシュされた応答を読み込む際に問題があるようです。そのため、リクエストがキャッシュされていて、リクエスト キャッシュ ポリシーがリクエストに設定されてNSURLRequestReturnCacheDataDontLoadいても失敗します。

そのため、特定のキャッシュ ポリシーに対してのみリクエストが失敗した場合に、キャッシュされたレスポンスを取得するように変更(void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)errorするという厄介な回避策があります。AFURLConnectionOperation.m

- (void)connection:(NSURLConnection __unused *)connection
  didFailWithError:(NSError *)error
{
    self.error = error;

    [self.outputStream close];

    [self finish];

    self.connection = nil;

    //Ugly hack for making the request succeed if we can find a valid non-empty cached request
    //This is because IOS6 is not handling cache responses right when we are in a no-connection sittuation
    //Only use this code for cache policies that are supposed to listen to cache regarding it's expiration date
    if (self.request.cachePolicy == NSURLRequestUseProtocolCachePolicy ||
        self.request.cachePolicy == NSURLRequestReturnCacheDataElseLoad ||
        self.request.cachePolicy == NSURLRequestReturnCacheDataDontLoad) {

        NSCachedURLResponse * cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
        if (cachedResponse.data.length > 0) {
            self.responseData = cachedResponse.data;
            self.response = cachedResponse.response;
            self.error = nil;
        }
    }
}
于 2013-04-08T17:30:07.037 に答える
0

HTTP ヘッダーなしでは多くを語ることはできませんが、これの最も一般的な理由は、NSURLProtocolキャッシュされた応答を WebView に配信する前に再検証を強制することです。

こちらをご覧ください: http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588

于 2012-06-17T15:43:07.843 に答える
0

キャッシュはデータの有効期限が切れており、サーバーから取得する必要があると言っていますが、リクエストを成功させたいようです。失敗するよりも古いデータを使用したい特定のリクエストのキャッシュ ポリシー (オンラインとオフラインで異なるポリシー) を設定すると、うまくいくかもしれません。

NSMutableURLRequest -> setCachePolicy

NSURLRequestReturnCacheDataDontLoadオフライン モードに必要なポリシーのようです。

それが役立つことを願っています!

于 2013-01-20T16:05:11.270 に答える