#import <UIKit/UIKit.h>
@interface WebViewController : UIViewController <UIWebViewDelegate> {
UIWebView *web;
UILabel *label;
}
@property (nonatomic, retain) UIWebView *web;
@property (nonatomic, retain) UILabel *label;
- (IBAction)click;
@end
#import "WebViewController.h"
#import "URLCache.h"
@implementation WebViewController
@synthesize web, label;
- (IBAction)click {
if (web) {
[web removeFromSuperview];
self.web = nil;
} else {
CGRect frame = {{0, 0}, {320, 380}};
UIWebView *webview = [[UIWebView alloc] initWithFrame:frame];
webview.scalesPageToFit = YES;
self.web = webview;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]];
[webview loadRequest:request];
[self.view addSubview:webview];
[webview release];
}
}
- (void)addButton {
CGRect frame = {{130, 400}, {60, 30}};
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = frame;
[button addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:@"我点" forState:UIControlStateNormal];
[self.view addSubview:button];
}
- (void)viewDidLoad {
[super viewDidLoad];
URLCache *sharedCache = [[URLCache alloc] initWithMemoryCapacity:1024 * 1024 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
CGRect frame = {{60, 200}, {200, 30}};
UILabel *textLabel = [[UILabel alloc] initWithFrame:frame];
textLabel.textAlignment = UITextAlignmentCenter;
[self.view addSubview:textLabel];
self.label = textLabel;
if (![sharedCache.responsesInfo count]) { // not cached
textLabel.text = @"缓存中…";
CGRect frame = {{0, 0}, {320, 380}};
UIWebView *webview = [[UIWebView alloc] initWithFrame:frame];
webview.delegate = self;
self.web = webview;
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]];
[webview loadRequest:request];
[webview release];
} else {
textLabel.text = @"已从硬盘读取缓存";
[self addButton];
}
[sharedCache release];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
self.web = nil;
label.text = @"请接通网络再运行本应用";
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.web = nil;
label.text = @"缓存完毕";
[self addButton];
URLCache *sharedCache = (URLCache *)[NSURLCache sharedURLCache];
[sharedCache saveInfo];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
if (!web) {
URLCache *sharedCache = (URLCache *)[NSURLCache sharedURLCache];
[sharedCache removeAllCachedResponses];
}
}
- (void)viewDidUnload {
self.web = nil;
self.label = nil;
}
- (void)dealloc {
[super dealloc];
[web release];
[label release];
}
@end
大部分的代码没什么要说的,随便挑2点。
#import <Foundation/Foundation.h>
@interface URLCache : NSURLCache {
NSMutableDictionary *cachedResponses;
NSMutableDictionary *responsesInfo;
}
@property (nonatomic, retain) NSMutableDictionary *cachedResponses;
@property (nonatomic, retain) NSMutableDictionary *responsesInfo;
- (void)saveInfo;
@end
#import "URLCache.h"
@implementation URLCache
@synthesize cachedResponses, responsesInfo;
- (void)removeCachedResponseForRequest:(NSURLRequest *)request {
NSLog(@"removeCachedResponseForRequest:%@", request.URL.absoluteString);
[cachedResponses removeObjectForKey:request.URL.absoluteString];
[super removeCachedResponseForRequest:request];
}
- (void)removeAllCachedResponses {
NSLog(@"removeAllObjects");
[cachedResponses removeAllObjects];
[super removeAllCachedResponses];
}
- (void)dealloc {
[cachedResponses release];
[responsesInfo release];
}
@end
static NSString *cacheDirectory;
+ (void)initialize {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
cacheDirectory = [[paths objectAtIndex:0] retain];
}
- (void)saveInfo {
if ([responsesInfo count]) {
NSString *path = [cacheDirectory stringByAppendingString:@"responsesInfo.plist"];
[responsesInfo writeToFile:path atomically: YES];
}
}
这里我用了stringByAppendingString:方法,更保险的是使用stringByAppendingPathComponent:。不过我估计后者会做更多的检查工作,所以采用了前者。
- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path {
if (self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) {
cachedResponses = [[NSMutableDictionary alloc] init];
NSString *path = [cacheDirectory stringByAppendingString:@"responsesInfo.plist"];
NSFileManager *fileManager = [[NSFileManager alloc] init];
if ([fileManager fileExistsAtPath:path]) {
responsesInfo = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
} else {
responsesInfo = [[NSMutableDictionary alloc] init];
}
[fileManager release];
}
return self;
}
static NSSet *supportSchemes;
+ (void)initialize {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
cacheDirectory = [[paths objectAtIndex:0] retain];
supportSchemes = [[NSSet setWithObjects:@"http", @"https", @"ftp", nil] retain];
}
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) {
return [super cachedResponseForRequest:request];
}
NSURL *url = request.URL;
if (![supportSchemes containsObject:url.scheme]) {
return [super cachedResponseForRequest:request];
}
//...
}
因为没必要处理它们,所以直接交给父类的处理方法了,它会自行决定是否返回nil的。
NSString *absoluteString = url.absoluteString;
NSLog(@"%@", absoluteString);
NSCachedURLResponse *cachedResponse = [cachedResponses objectForKey:absoluteString];
if (cachedResponse) {
NSLog(@"cached: %@", absoluteString);
return cachedResponse;
}
NSDictionary *responseInfo = [responsesInfo objectForKey:absoluteString];
if (responseInfo) {
NSString *path = [cacheDirectory stringByAppendingString:[responseInfo objectForKey:@"filename"]];
NSFileManager *fileManager = [[NSFileManager alloc] init];
if ([fileManager fileExistsAtPath:path]) {
[fileManager release];
NSData *data = [NSData dataWithContentsOfFile:path];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[responseInfo objectForKey:@"MIMEType"] expectedContentLength:data.length textEncodingName:nil];
cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
[response release];
[cachedResponses setObject:cachedResponse forKey:absoluteString];
[cachedResponse release];
NSLog(@"cached: %@", absoluteString);
return cachedResponse;
}
[fileManager release];
}
这里的难点在于构造NSURLResponse和NSCachedURLResponse,不过对照下文档看看也就清楚了。如前文所说,我们还得把cachedResponse保存到cachedResponses里,避免它被提前释放。
NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:request.timeoutInterval];
newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
newRequest.HTTPShouldHandleCookies = request.HTTPShouldHandleCookies;
实际上NSMutableURLRequest还有一些其他的属性,不过并不太重要,所以我就只复制了这2个。
NSError *error = nil;
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
if (error) {
NSLog(@"%@", error);
NSLog(@"not cached: %@", absoluteString);
return nil;
}
NSString *filename = sha1([absoluteString UTF8String]);
NSString *path = [cacheDirectory stringByAppendingString:filename];
NSFileManager *fileManager = [[NSFileManager alloc] init];
[fileManager createFileAtPath:path contents:data attributes:nil];
[fileManager release];
NSURLResponse *newResponse = [[NSURLResponse alloc] initWithURL:response.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
responseInfo = [NSDictionary dictionaryWithObjectsAndKeys:filename, @"filename", newResponse.MIMEType, @"MIMEType", nil];
[responsesInfo setObject:responseInfo forKey:absoluteString];
NSLog(@"saved: %@", absoluteString);
cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse data:data];
[newResponse release];
[cachedResponses setObject:cachedResponse forKey:absoluteString];
[cachedResponse release];
return cachedResponse;
还不放心的话可以退出程序,这样内存缓存肯定就释放了。然后再次进入并打开网页,你会发现一切仍然正常~
地址:http://www.keakon.net/2011/08/14/%E4%B8%BAUIWebView%E5%AE%9E%E7%8E%B0%E7%A6%BB%E7%BA%BF%E6%B5%8F%E8%A7%88