[iOS] UIWebView のキャッシュ調査

2011年1月14日金曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

UIWebView のキャッシュについて少し調べた。

概要


[関連情報] Cocoaの日々: サーバ上のファイルを見るには?

UIWebView は指定されたリクエストに従いコンテンツをダウンロードする。この時にキャッシュをメモリに保存する。

キャッシュの制御には UIWebViewに渡す NSURLRequest で行う。
(例)
 NSURLRequest *request = [NSURLRequest requestWithURL:url
   cachePolicy:NSURLRequestReturnCacheDataElseLoad
   timeoutInterval:60.0];
キャッシュポリシーは次の種類がある。NSURLRequst.h より転載。
enum
{
    NSURLRequestUseProtocolCachePolicy = 0,

    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    NSURLRequestReturnCacheDataElseLoad = 2,
    NSURLRequestReturnCacheDataDontLoad = 3,

    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};
typedef NSUInteger NSURLRequestCachePolicy;
デフォルトでは NSURLRequestUseProtocolCachePolicy になっている。


キャッシュの仕組み


キャッシュは URL Loading System の一部として働く。
URL Loading System Programming Guide: URL Loading System Overview

キャッシュ動作については以前 Mac OS X で調査したことがある。
(旧) Cocoaの日々: キャッシュ調査(さらに続く)

iOSでも同等の仕組みを使っているので基本的には同じ動作となる。以下、要点を抜粋:
・キャッシュはディスクとメモリを使う。
・キャッシュはアプリケーションごとに格納される。
・キャッシュは NSURLConnection もしくは NSURLDownload によって使われる
・キャッシュポリシーは NSURLRequest 初期化時に決められる
・NSURLCache クラスを使ってキャッシュサイズや、ディスク上の保存位置を設定できる
・NSCachedURLResponse を使うとキャッシュされたコンテンツへアクセスすることができる
・NSCachedURLResponse は NSURLResponse とコンテンツデータをカプセル化している
・現行は http/https のリクエストがキャッシュされる
 (httpsリクエストはディスクにキャッシュされることはない)
・NSURLConnection の connection:willCacheResponse: デリゲートメソッドを使って
 キャッシュの挙動を制御することができる
iOS の場合は下記の点が異なる。
・ディスクキャッシュは使わない
・-[NSURLCache cachedResponseForRequest:] をオーバーライドして
 キャッシュの挙動を制御することができる。
ディスクキャッシュを使わないことはリファレンスでは確認できなかったが、いくつかのブログで指摘されていた。

A-Liaison BLOG: iPhoneやiPhoneシミュレータ上でNSURLCacheクラスを使う
iPhone実機では、メモリ上へのキャッシュは働くがファイル上へのキャッシュは行われない。したがってアプリを終了するとキャッシュはすべて消える。
objective c - iphone: NSURLCache on disk - Stack Overflow
AFAIK NSURLRequest/NSURLConnection on iOS doesn't support caching to disk.
iPhoneシミュレータではディスクキャッシュが残るらしい。


応用(1) カスタムキャッシュ


UIWebView は暗黙的に NSURLCache を使用している。
NSURLCache Class Reference

通常はシステムがインスタンスを提供していて +sharedURLCache で利用することができる。このインスタンスは +setSharedURLCache: で置換することができるので、キャッシュ動作をカスタマイズしたい場合は自分で NSURLCache のサブクラスを用意し、そのインスタンスを設定することで URL Loading System(すわなわちそれを利用する UIWebView)のキャッシュの振る舞いをカスタマイズすることができる。
@interface CustomCache : NSURLCache
{
  :
}
@end
設定はアプリケーション起動直後などで行う。
- (BOOL)application:(UIApplication *)application 
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    :
    CustomCache *cache =
      [[CustomCache alloc] initWithMemoryCapacity: memoryCapacity
                         diskCapacity: discCapacity diskPath:path];
    [NSURLCache setSharedURLCache:cache];
    [cache release];
}

UIWebView はコンテンツを取得する場合、次の動作を行う。
1. loadRequest: が呼ばれる
2. UIWebView内で -[NSURLCache cachedResponseForRequest:] を呼び出す。
 a. 戻り値の NSCachedURLResponse(キャッシュされたコンテンツ)が取得できればそれを利用する。
 b. 戻り値が nil(キャッシュ無し)ならネットワーク経由で最新のコンテンツを取得する。
この為、NSURLCahce のサブクラスでは -[NSURLCache cachedResponseForRequest:] をオーバーライドして、ここにキャッシュロジックを記述する。iOS ではディスクキャッシュを使わないが、必要なら自前でディスクキャッシュをここに実装することもできる。またUIWebView を使ったオフライン動作するようなアプリもこれを使うと組めそうだ。

カスタムキャッシュクラスの作成方法は下記のページが役に立つ。
IPHONE - How to save the content in UIWebView for faster loading on next launch? - efreedom

応用例まとめ


  • カスタムキャッシュロジックの実装(ディスクキャッシュなど)
  • UIWebView のオフライン動作(キャシュコンテンツの強制利用、HTML中のリソースリンクの書き換えなど)



応用(2) コンテンツフィルタ


NSURLCache のサブクラス化を応用するとコンテンツフィルタを実装することができる。コンテンツフィルタを行う方法は下記が参考になる。
URL filtering for UIWebView on the iPhone – iCab Blog

(cachedResponseForRequest: の実装を転載)
- (NSCachedURLResponse*)cachedResponseForRequest:(NSURLRequest*)request
{
    NSURL *url = [request URL];
    BOOL blockURL = [[FilterMgr sharedFilterMgr] shouldBlockURL:url];
    if (blockURL) {
        NSURLResponse *response =
              [[NSURLResponse alloc] initWithURL:url
                                        MIMEType:@"text/plain"
                           expectedContentLength:1
                                textEncodingName:nil];

        NSCachedURLResponse *cachedResponse =
              [[NSCachedURLResponse alloc] initWithResponse:response
                             data:[NSData dataWithBytes:" " length:1]];

        [super storeCachedResponse:cachedResponse forRequest:request];

        [cachedResponse release];
        [response release];
    }
    return [super cachedResponseForRequest:request];
}
FilterMgr によてブロックすべきURLと判断した場合は空白のページを返す様になっている。つまりURLの実体とは無関係に、プログラムで勝手に表示したいコンテンツをキャッシュと偽装して UIWebViewへ渡すことでフィルタリングを実現している。
このアイディアは面白い。この方法を使えばコンテンツ中のタグの書き換えも容易にできる(実際にその用途でよく利用されているらしい)。


応用例まとめ

  • コンテンツの差し替え
  • コンテンツの書き換え(リンクの書き換え、メッセージの書き換え)



参考情報


iPhone, Android, webOS モバイルブラウザキャッシュの制限 - WebOS Goodies
モバイル Safari 他のキャッシュについての調査。

(旧) Cocoaの日々: WebKit検証(32) - キャッシュの中身
Cache.db の調査。

(旧) Cocoaの日々: WebKit検証(36) - キャッシュをクリアする(成功)

NSURLCacheについてのメモ - iRSSの日記
キャッシュはLast-ModifiedやExpiresヘッダがないと機能しないのである。

Cocoa with Love: Substituting local data for remote UIWebView requests

Responses

Leave a Response

人気の投稿(過去 30日間)