本地缓存机制之一
在手機應(yīng)用程序開發(fā)中,為了減少與服務(wù)端的交互次數(shù),加快用戶的響應(yīng)速度,一般都會在iOS設(shè)備中加一個緩存的機制,前面一篇文章介紹了iOS設(shè)備的內(nèi)存緩存,這篇文章將設(shè)計一個本地緩存的機制。
?
功能需求
?
? ?? ? 這個緩存機制滿足下面這些功能。
?
1、可以將數(shù)據(jù)緩存到本地磁盤。
?
2、可以判斷一個資源是否已經(jīng)被緩存。如果已經(jīng)被緩存,在請求相同的資源,先到本地磁盤搜索。
?
3、可以判斷文件緩存什么時候過期。這里為了簡單起見這里,我們在請求url資源的時候,給每次請求的文件設(shè)定一個過期的時間。
?
4、可以實現(xiàn):如果文件已經(jīng)被緩存,而且沒有過期,這將本地的數(shù)據(jù)返回,否則重新請求url。
?
5、可以實現(xiàn):如果文件下載不成功或者下載沒有完成,下次打開程序的時候,移除這些沒有成功或者沒有下載完成的文件。
?
6、可以實現(xiàn):同時請求或者下載多個資源。
?
設(shè)計實現(xiàn):
?
1、設(shè)計一個CacheItem類,用來請求一個web連接,它的一個實例表示一個緩存項。這個CacheItem類,需要一個url創(chuàng)建一個NSURLConnection,去請求web資源。使用CacheItem類主要用來請求web資源。
/* ---------緩存項-------------- */??
? ?
@interface CacheItem : NSObject {??
@public??
? ?id<CacheItemDelegate> delegate;??
? ???//web地址??
? ?NSString? ?? ?? ?? ???*remoteURL;??
@private??
? ???//是否正在下載??
? ?BOOL? ?? ?? ?? ?? ?? ?isDownloading;??
? ?? ???//NSMutableData對象??
? ?NSMutableData? ?? ?? ?*connectionData;??
? ???//NSURLConnection對象??
? ?NSURLConnection? ?? ? *connection;??
}??
? ?
/* -------------------------- */??
? ?
@property (nonatomic, retain) id<CacheItemDelegate> delegate;??
@property (nonatomic, retain) NSString??*remoteURL;??
@property (nonatomic, assign) BOOL? ?? ?isDownloading;??
@property (nonatomic, retain) NSMutableData *connectionData;??
@property (nonatomic, retain) NSURLConnection *connection;??
? ?
/* ----------開始下載方法----------- */??
? ?
- (BOOL) startDownloadingURL:(NSString *)paramRemoteURL;??
? ?
@end
復(fù)制代碼
2、在NSURLConnection開始請求之前,調(diào)用CachedDownloadManager類,來搜索和管理本地的緩存文件。將緩存文件的情況保存到一個字典類中。這個字典設(shè)計如下:
{??
? ?"http://www.cnn.com" =? ???{??
? ???DownloadEndDate = "2011-08-02 07:51:57 +0100";??
? ???DownloadStartDate = "2011-08-02 07:51:55 +0100";??
? ???ExpiresInSeconds = 20;??
? ???ExpiryDate = "2011-08-02 07:52:17 +0100";??
? ???LocalURL = "/var/mobile/Applications/ApplicationID/Documents/??
? ?? ?? ?? ?? ???httpwww.cnn.com.cache";??
? ?};??
? ?"http://www.baidu.com" =? ???{??
? ???DownloadEndDate = "2011-08-02 07:51:49 +0100";??
? ???DownloadStartDate = "2011-08-02 07:51:44 +0100";??
? ???ExpiresInSeconds = 20;??
? ???ExpiryDate = "2011-08-02 07:52:09 +0100";??
? ???LocalURL = "/var/mobile/Applications/ApplicationID/Documents/??
? ?? ?? ?? ?? ???httpwww.oreilly.com.cache";??
? ?};??
}
復(fù)制代碼
? ?? ? 上面這個字典里面嵌套了字典。里面那層字典表示一個緩存項的緩存信息:下載結(jié)束時間、下載開始時間、緩存有效時間、緩存過期時間、緩存到本地的路徑。
? ?? ? 下面看下CachedDownloadManager類。用它來實現(xiàn)和封裝我們的緩存策略。
/* -----------CachedDownloadManager-------------- */??
? ?
@interface CachedDownloadManager : NSObject? ?
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?<CacheItemDelegate> {??
@public??
? ?id<CachedDownloadManagerDelegate>??delegate;??
@private??
//記錄緩存數(shù)據(jù)的字典??
? ?NSMutableDictionary? ?? ?? ?? ?? ? *cacheDictionary;??
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //緩存的路徑??
? ?NSString? ?? ?? ?? ?? ?? ?? ?? ?? ?*cacheDictionaryPath;??
}??
? ?
? ?
@property (nonatomic, assign)? ?
id<CachedDownloadManagerDelegate> delegate;??
? ?
@property (nonatomic, copy)? ?
NSMutableDictionary *cacheDictionary;??
? ?
@property (nonatomic, retain)? ?
NSString *cacheDictionaryPath;??
? ?
? ?
/* 保持緩存字典 */??
? ?
- (BOOL) saveCacheDictionary;??
? ?
/* 公有方法:下載 */??
? ?
- (BOOL)? ?? ?? ?download:(NSString *)paramURLAsString??
? ? urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds??
updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache;??
? ?
/* -------------------------- */??
? ?
@end
復(fù)制代碼
?
? ?? ? 從上面代碼可以看出,這個管理緩存的類中,有一個緩存字典:cacheDictionary,用來表示所有資源的緩存情 況;cacheDictionaryPath用來表示緩存的路徑;saveCacheDictionary用來將緩存字典歸檔到本地文件中。 download:urlMustExpireInSeconds:updateExpiryDateIfInCache是一個公共接口,通過傳遞 url、緩存過期時間、是否更新緩存過期時間三個參數(shù)來方便的使用,實現(xiàn)我們的緩存策略。
?
3、如果這個文件已經(jīng)被下載,而且沒有過期,則從本地獲取文件的數(shù)據(jù)。如果文件已經(jīng)過期,則重新下載。我們通過download:urlMustExpireInSeconds:updateExpiryDateIfInCache方法來實現(xiàn),主要看這個方法的代碼:
/* ---------下載-------------- */??
? ?
- (BOOL)? ?? ?? ?download:(NSString *)paramURLAsString??
? ? urlMustExpireInSeconds:(NSTimeInterval)paramURLMustExpireInSeconds??
updateExpiryDateIfInCache:(BOOL)paramUpdateExpiryDateIfInCache{??
? ???
? ?BOOL result = NO;??
? ???
? ?if (self.cacheDictionary == nil ||??
? ?? ? [paramURLAsString length] == 0){??
? ???return(NO);??
? ?}??
? ???
? ?paramURLAsString = [paramURLAsString lowercaseString];??
? ?//根據(jù)url,從字典中獲取緩存項的相關(guān)數(shù)據(jù)??
? ?NSMutableDictionary *itemDictionary =? ?
? ?[self.cacheDictionary objectForKey:paramURLAsString];??
? ???
? ?/* 使用下面這些變量幫助我們理解緩存邏輯 */??
? ???//文件是否已經(jīng)被緩存??
? ?BOOL? ? fileHasBeenCached = NO;??
? ???//緩存是否過期??
? ?BOOL? ? cachedFileHasExpired = NO;??
? ???//緩存文件是否存在??
? ?BOOL? ? cachedFileExists = NO;??
? ???//緩存文件能否被加載??
? ?BOOL? ? cachedFileDataCanBeLoaded = NO;??
? ???//緩存文件數(shù)據(jù)??
? ?NSData??*cachedFileData = nil;??
? ???//緩存文件是否完全下載??
? ?BOOL? ? cachedFileIsFullyDownloaded = NO;??
? ???//緩存文件是否已經(jīng)下載??
? ?BOOL? ? cachedFileIsBeingDownloaded = NO;??
? ?//過期時間??
? ?NSDate? ? *expiryDate = nil;??
? ???//下載結(jié)束時間??
? ?NSDate? ? *downloadEndDate = nil;??
? ???//下載開始時間??
? ?NSDate? ? *downloadStartDate = nil;??
? ???//本地緩存路徑??
? ?NSString??*localURL = nil;??
? ???//有效時間??
? ?NSNumber??*expiresInSeconds = nil;??
? ?NSDate? ? *now = [NSDate date];??
? ???
? ?if (itemDictionary != nil){??
? ???fileHasBeenCached = YES;??
? ?}??
? ?//如果文件已經(jīng)被緩存,則從緩存項相關(guān)數(shù)據(jù)中獲取相關(guān)的值??
? ?if (fileHasBeenCached == YES){??
? ?? ??
? ???expiryDate = [itemDictionary? ?
? ?? ?? ?? ?? ?? ? objectForKey:CachedKeyExpiryDate];??
? ?? ??
? ???downloadEndDate = [itemDictionary??
? ?? ?? ?? ?? ?? ?? ?? ?objectForKey:CachedKeyDownloadEndDate];??
? ?? ??
? ???downloadStartDate = [itemDictionary??
? ?? ?? ?? ?? ?? ?? ?? ???objectForKey:CachedKeyDownloadStartDate];??
? ?? ??
? ???localURL = [itemDictionary??
? ?? ?? ?? ?? ???objectForKey:CachedKeyLocalURL];??
? ?? ??
? ???expiresInSeconds = [itemDictionary??
? ?? ?? ?? ?? ?? ?? ?? ? objectForKey:CachedKeyExpiresInSeconds];??
? ???//如果下載開始和結(jié)束時間不為空,表示文件全部被下載??
? ???if (downloadEndDate != nil &&? ?
? ?? ?? ?downloadStartDate != nil){??
? ?? ? cachedFileIsFullyDownloaded = YES;??
? ???}??
? ?? ??
? ???/* 如果expiresInSeconds不為空,downloadEndDate為空,表示文件已經(jīng)正在下載 */??
? ???if (expiresInSeconds != nil &&??
? ?? ?? ?downloadEndDate == nil){??
? ?? ? cachedFileIsBeingDownloaded = YES;??
? ???}??
? ?? ??
? ???/* 判斷緩存是否過期 */??
? ???if (expiryDate != nil &&??
? ?? ?? ?[now timeIntervalSinceDate:expiryDate] > 0.0){??
? ?? ? cachedFileHasExpired = YES;??
? ???}??
? ?? ??
? ???if (cachedFileHasExpired == NO){??
? ?? ? /* 如果緩存文件沒有過期,加載緩存文件,并且更新過期時間 */??
? ?? ? NSFileManager *fileManager = [[NSFileManager alloc] init];??
? ?? ?? ?
? ?? ? if ([fileManager fileExistsAtPath:localURL] == YES){??
? ?? ?? ?cachedFileExists = YES;??
? ?? ?? ?cachedFileData = [NSData dataWithContentsOfFile:localURL];??
? ?? ?? ?if (cachedFileData != nil){??
? ?? ?? ???cachedFileDataCanBeLoaded = YES;??
? ?? ?? ?} /* if (cachedFileData != nil){ */??
? ?? ? } /* if ([fileManager fileExistsAtPath:localURL] == YES){ */??
? ?? ?? ?
? ?? ? [fileManager release];??
? ?? ?? ?
? ?? ? /* 更新緩存時間 */??
? ?? ?? ?
? ?? ? if (paramUpdateExpiryDateIfInCache == YES){??
? ?? ?? ???
? ?? ?? ?NSDate *newExpiryDate =? ?
? ?? ?? ?[NSDate dateWithTimeIntervalSinceNow:??
? ?? ?? ? paramURLMustExpireInSeconds];??
? ?? ?? ???
? ?? ?? ?NSLog(@"Updating the expiry date from %@ to %@.",? ?
? ?? ?? ?? ?? ?expiryDate,? ?
? ?? ?? ?? ?? ?newExpiryDate);??
? ?? ?? ???
? ?? ?? ?[itemDictionary setObject:newExpiryDate??
? ?? ?? ?? ?? ?? ?? ?? ?? ? forKey:CachedKeyExpiryDate];??
? ?? ?? ???
? ?? ?? ?NSNumber *expires =? ?
? ?? ?? ?[NSNumber numberWithFloat:paramURLMustExpireInSeconds];??
? ?? ?? ???
? ?? ?? ?[itemDictionary setObject:expires??
? ?? ?? ?? ?? ?? ?? ?? ?? ? forKey:CachedKeyExpiresInSeconds];??
? ?? ? }??
? ?? ?? ?
? ???} /* if (cachedFileHasExpired == NO){ */??
? ?? ??
? ?}??
? ???
? ?if (cachedFileIsBeingDownloaded == YES){??
? ???NSLog(@"這個文件已經(jīng)正在下載...");??
? ???return(YES);??
? ?}??
? ???
? ?if (fileHasBeenCached == YES){??
? ?? ??
? ???if (cachedFileHasExpired == NO &&??
? ?? ?? ?cachedFileExists == YES &&??
? ?? ?? ?cachedFileDataCanBeLoaded == YES &&??
? ?? ?? ?[cachedFileData length] > 0 &&??
? ?? ?? ?cachedFileIsFullyDownloaded == YES){??
? ?? ?? ?
? ?? ? /* 如果文件有緩存而且沒有過期 */??
? ?? ?? ?
? ?? ? NSLog(@"文件有緩存而且沒有過期.");??
? ?? ?? ?
? ?? ? [self.delegate? ?
? ?? ???cachedDownloadManagerSucceeded:self??
? ?? ???remoteURL:[NSURL URLWithString:paramURLAsString]??
? ?? ???localURL:[NSURL URLWithString:localURL]??
? ?? ???aboutToBeReleasedData:cachedFileData??
? ?? ???isCachedData:YES];??
? ?? ?? ?
? ?? ? return(YES);??
? ?? ?? ?
? ???} else {??
? ?? ? /* 如果文件沒有被緩存,獲取緩存失敗 */??
? ?? ? NSLog(@"文件沒有緩存.");??
? ?? ? [self.cacheDictionary removeObjectForKey:paramURLAsString];??
? ?? ? [self saveCacheDictionary];??
? ???} /* if (cachedFileHasExpired == NO && */??
? ?? ??
? ?} /* if (fileHasBeenCached == YES){ */??
? ???
? ?/* 去下載文件 */??
? ???
? ???
? ?NSNumber *expires =? ?
? ?[NSNumber numberWithFloat:paramURLMustExpireInSeconds];??
? ???
? ?NSMutableDictionary *newDictionary =? ?
? ?[[[NSMutableDictionary alloc] init] autorelease];??
? ???
? ?[newDictionary setObject:expires? ?
? ?? ?? ?? ?? ?? ?? ?forKey:CachedKeyExpiresInSeconds];??
? ???
? ???
? ?localURL = [paramURLAsString??
? ?? ?? ?? ?? ?stringByAddingPercentEscapesUsingEncoding:??
? ?? ?? ?? ?? ?NSUTF8StringEncoding];??
? ???
? ?localURL = [localURL stringByReplacingOccurrencesOfString:@"://"??
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???withString:@""];??
? ???
? ?localURL = [localURL stringByReplacingOccurrencesOfString:@"/"??
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???withString:@"{1}quot;];??
? ???
? ?localURL = [localURL stringByAppendingPathExtension:@"cache"];??
? ???
? ?NSString *documentsDirectory =? ?
? ?[self documentsDirectoryWithTrailingSlash:NO];??
? ???
? ?localURL = [documentsDirectory? ?
? ?? ?? ?? ?? ?stringByAppendingPathComponent:localURL];??
? ???
? ?[newDictionary setObject:localURL??
? ?? ?? ?? ?? ?? ?? ?forKey:CachedKeyLocalURL];??
? ???
? ?[newDictionary setObject:now??
? ?? ?? ?? ?? ?? ?? ?forKey:CachedKeyDownloadStartDate];??
? ???
? ?[self.cacheDictionary setObject:newDictionary??
? ?? ?? ?? ?? ?? ?? ?? ?? ? forKey:paramURLAsString];??
? ???
? ?[self saveCacheDictionary];??
? ???
? ?CacheItem *item = [[[CacheItem alloc] init] autorelease];??
? ?[item setDelegate:self];??
? ?[item startDownloadingURL:paramURLAsString];??
? ???
? ?return(result);??
? ???
}
文章來源:http://www.cnblogs.com/pengyingh/articles/2343039.html
分類: iOS
轉(zhuǎn)載于:https://blog.51cto.com/9077272/1428594
總結(jié)
- 上一篇: Toad 补充与培训 常用菜单
- 下一篇: 如何在servlet刚启动时候获取服务器