SDMemoryCache 源碼分析
上次我們分析過 AFN 的源碼,也分析過 AFN 的 內(nèi)存緩存,這里再分析下 SD 的緩存,AFN 是 自己設(shè)計的緩存晚唇,然后超過了預(yù)期的大小之后,開始循環(huán)清除盗似,直到剩下預(yù)期可接受的大小為止哩陕,這里 SD 的緩存策略怎么設(shè)計的呢?
/**
A memory cache which auto purge the cache on memory warning and support weak cache.
*/
@interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType> <SDMemoryCache>
@property (nonatomic, strong, nonnull, readonly) SDImageCacheConfig *config;
@end
這里是集成系統(tǒng) NSCache 的赫舒,并且是個泛型類悍及,實現(xiàn) SDMemoryCache 這個協(xié)議,這個協(xié)議也就告訴我們接癌,我們可以自定義實現(xiàn)內(nèi)存緩存心赶,實現(xiàn)協(xié)議的這些方法就可以。
}
- (void)commonInit {
SDImageCacheConfig *config = self.config;
self.totalCostLimit = config.maxMemoryCost;
self.countLimit = config.maxMemoryCount;
[config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCost)) options:0 context:SDMemoryCacheContext];
[config addObserver:self forKeyPath:NSStringFromSelector(@selector(maxMemoryCount)) options:0 context:SDMemoryCacheContext];
#if SD_UIKIT
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
self.weakCacheLock = dispatch_semaphore_create(1);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
#endif
}
SDImageCacheConfig 是一個配置缺猛,可以設(shè)置緩存的數(shù)量限制和成本內(nèi)存限制缨叫,當(dāng)前類是繼承自NSCache這個類的,所以直接設(shè)置給父類就可以荔燎,并且當(dāng)前緩存類還建立了一個弱緩存耻姥,是基于 NSMapTable 數(shù)據(jù)結(jié)構(gòu)的,key 是 strong 湖雹,value 是 weak 的,然后監(jiān)聽系統(tǒng)的內(nèi)存警告曙搬,NSCache 是不能自動監(jiān)聽內(nèi)存警告的摔吏,當(dāng)收到內(nèi)存警告的時候,會清楚系統(tǒng) nscache 存儲的所有數(shù)據(jù)
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
// Only remove cache, but keep weak cache
[super removeAllObjects];
}
// `setObject:forKey:` just call this with 0 cost. Override this is enough
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
[super setObject:obj forKey:key cost:g];
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
if (key && obj) {
// Store weak cache
SD_LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
SD_UNLOCK(self.weakCacheLock);
}
}
- (id)objectForKey:(id)key {
id obj = [super objectForKey:key];
if (!self.config.shouldUseWeakMemoryCache) {
return obj;
}
if (key && !obj) {
// Check weak cache
SD_LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
SD_UNLOCK(self.weakCacheLock);
if (obj) {
// Sync cache
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
cost = [(UIImage *)obj sd_memoryCost];
}
[super setObject:obj forKey:key cost:cost];
}
}
return obj;
}
- (void)removeObjectForKey:(id)key {
[super removeObjectForKey:key];
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
if (key) {
// Remove weak cache
SD_LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
SD_UNLOCK(self.weakCacheLock);
}
}
- (void)removeAllObjects {
[super removeAllObjects];
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
// Manually remove should also remove weak cache
SD_LOCK(self.weakCacheLock);
[self.weakCache removeAllObjects];
SD_UNLOCK(self.weakCacheLock);
}
添加刪除纵装,都是通過信號量來控制線程安全的征讲,可以看到?jīng)]有給父類 nscache 加鎖,因為系統(tǒng)的nscache是 線程安全的橡娄,我們可以看到設(shè)置值會先給父類設(shè)置诗箍,如果配置沒有設(shè)置弱引用緩存直接返回,如果設(shè)置了挽唉,就往弱引用緩存里面添加一份滤祖,獲取值的時候先從父類里面取,如果配置沒有設(shè)置從弱引用取的話瓶籽,直接返回匠童,如果設(shè)置了,接著判斷塑顺,nscache是否能夠獲取到汤求,因為俏险,nscache 會根據(jù)系統(tǒng)自己的策略,自己清楚緩存扬绪,這時候就從我們的弱引用緩存中獲取竖独,獲取到了之后,再往nscache里面放一份挤牛,只有從nscache沒有獲取到才會執(zhí)行此操作莹痢,可以看出,set方法是nscache和自己的弱引用緩存都存儲赊颠,get的時候格二,如果nscache沒有,才從弱引用緩存中去竣蹦,然后存儲到nscache中顶猜,
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == SDMemoryCacheContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCost))]) {
self.totalCostLimit = self.config.maxMemoryCost;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(maxMemoryCount))]) {
self.countLimit = self.config.maxMemoryCount;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
對于 kvo ,監(jiān)聽totalCostLimit和maxMemoryCost的設(shè)置痘括,然后動態(tài)設(shè)置給nscache长窄。
這就是 SD 的內(nèi)存緩存,要比 AFN 還要簡單纲菌,但我會提出如下幾點疑問挠日,歡迎評論區(qū)發(fā)表意見
- 為什么要用 NSCache 和 自己再創(chuàng)建一個弱引用緩存,要存儲兩份翰舌?就用弱引用緩存不行嗎嚣潜?
- 信號量這里面是單讀單寫的,沒有多讀單寫性能好椅贱,這里是有什么考慮嗎懂算?
SDDiskCache 源碼分析
最開始我理解的內(nèi)存緩存和磁盤緩存,我以為磁盤緩存會像 YYCache 一樣庇麦,存儲到數(shù)據(jù)庫计技,過段時間寫兩片 YYCache 的文章,結(jié)果看到代碼后山橄,我發(fā)現(xiàn)我錯了垮媒,我們看他的初始化,你就明白了
- (void)commonInit {
if (self.config.fileManager) {
self.fileManager = self.config.fileManager;
} else {
self.fileManager = [NSFileManager new];
}
}
如果看到這里你還不明白怎么存儲航棱,那就往后看吧
- (BOOL)containsDataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
BOOL exists = [self.fileManager fileExistsAtPath:filePath];
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
if (!exists) {
exists = [self.fileManager fileExistsAtPath:filePath.stringByDeletingPathExtension];
}
return exists;
}
- (NSData *)dataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
NSData *data = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
// fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
// checking the key with and without the extension
data = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
return nil;
}
- (void)setData:(NSData *)data forKey:(NSString *)key {
NSParameterAssert(data);
NSParameterAssert(key);
if (![self.fileManager fileExistsAtPath:self.diskCachePath]) {
[self.fileManager createDirectoryAtPath:self.diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// get cache Path for image key
NSString *cachePathForKey = [self cachePathForKey:key];
// transform to NSURL
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
[data writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
// disable iCloud backup
if (self.config.shouldDisableiCloud) {
// ignore iCloud backup resource value error
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
- (NSData *)extendedDataForKey:(NSString *)key {
NSParameterAssert(key);
// get cache Path for image key
NSString *cachePathForKey = [self cachePathForKey:key];
NSData *extendedData = [SDFileAttributeHelper extendedAttribute:SDDiskCacheExtendedAttributeName atPath:cachePathForKey traverseLink:NO error:nil];
return extendedData;
}
- (void)setExtendedData:(NSData *)extendedData forKey:(NSString *)key {
NSParameterAssert(key);
// get cache Path for image key
NSString *cachePathForKey = [self cachePathForKey:key];
if (!extendedData) {
// Remove
[SDFileAttributeHelper removeExtendedAttribute:SDDiskCacheExtendedAttributeName atPath:cachePathForKey traverseLink:NO error:nil];
} else {
// Override
[SDFileAttributeHelper setExtendedAttribute:SDDiskCacheExtendedAttributeName value:extendedData atPath:cachePathForKey traverseLink:NO overwrite:YES error:nil];
}
}
- (void)removeDataForKey:(NSString *)key {
NSParameterAssert(key);
NSString *filePath = [self cachePathForKey:key];
[self.fileManager removeItemAtPath:filePath error:nil];
}
- (void)removeAllData {
[self.fileManager removeItemAtPath:self.diskCachePath error:nil];
[self.fileManager createDirectoryAtPath:self.diskCachePath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
}
這里是對數(shù)據(jù)的增刪查的處理睡雇,get 方法是通過 dataWithContentsOfFile
api,set 是通過 [data writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil]; 這個 api饮醇,然后根據(jù)配置判斷是否進(jìn)行 iCloud 存儲
// ignore iCloud backup resource value error
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
刪除是通過 [self.fileManager removeItemAtPath:filePath error:nil];
api ,都是通過系統(tǒng) api入桂,接下來我們來看看是怎么處理過期數(shù)據(jù)的
// 移除過期數(shù)據(jù)
- (void)removeExpiredData {
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
// Compute content date key to be used for tests
// 過期時間的依據(jù),訪問時間驳阎,內(nèi)容修改時間抗愁,創(chuàng)建時間或者屬性修改時間
NSURLResourceKey cacheContentDateKey = NSURLContentModificationDateKey;
switch (self.config.diskCacheExpireType) {
case SDImageCacheConfigExpireTypeAccessDate:
cacheContentDateKey = NSURLContentAccessDateKey;
break;
case SDImageCacheConfigExpireTypeModificationDate:
cacheContentDateKey = NSURLContentModificationDateKey;
break;
case SDImageCacheConfigExpireTypeCreationDate:
cacheContentDateKey = NSURLCreationDateKey;
break;
case SDImageCacheConfigExpireTypeChangeDate:
cacheContentDateKey = NSURLAttributeModificationDateKey;
break;
default:
break;
}
// 想要了解的屬性
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, cacheContentDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
// 遍歷文件馁蒂,跳過隱藏文件
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
// 過期時間
NSDate *expirationDate = (self.config.maxDiskAge < 0) ? nil: [NSDate dateWithTimeIntervalSinceNow:-self.config.maxDiskAge];
NSMutableDictionary<NSURL *, NSDictionary<NSString *, id> *> *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
NSMutableArray<NSURL *> *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
// 根據(jù) url 獲取返回結(jié)果,從中獲取想要或崎嶇的屬性
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
// Skip directories and errors.
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
// 獲取修改時間
NSDate *modifiedDate = resourceValues[cacheContentDateKey];
// 過期了
if (expirationDate && [[modifiedDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
// 將文件總大小存儲下來
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
// 將過期文件刪除
for (NSURL *fileURL in urlsToDelete) {
[self.fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
NSUInteger maxDiskSize = self.config.maxDiskSize;
// 如果當(dāng)前磁盤存儲文件的總大小大于配置的大小
if (maxDiskSize > 0 && currentCacheSize > maxDiskSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = maxDiskSize / 2;
// Sort the remaining cache files by their last modification time or last access time (oldest first).
// 將剩余的文件按照訪問時間進(jìn)行排序,最老的放在最前面
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[cacheContentDateKey] compare:obj2[cacheContentDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self.fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
// 挨個刪除蜘腌,知道大小達(dá)到我們預(yù)期的值沫屡,也就是設(shè)置的最大值的一半
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
}
先是根據(jù)配置,設(shè)置選擇文件的訪問時間撮珠,是以何種策略來淘汰文件沮脖,是根據(jù)創(chuàng)建時間超時,還是內(nèi)容修改時間超時等依據(jù)芯急,然后創(chuàng)建一個 數(shù)據(jù)勺届,想要查看文件的哪些屬性 NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, cacheContentDateKey, NSURLTotalFileAllocatedSizeKey];
,這里是查看文件是否為目錄,訪問時間屬性娶耍,和文件的總大小屬性免姿,然后拿出所有文件的 url
// 遍歷文件,跳過隱藏文件
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
拿到總的結(jié)果之后榕酒,然后根據(jù)配置算出真正的過期時間
// 過期時間
NSDate *expirationDate = (self.config.maxDiskAge < 0) ? nil: [NSDate dateWithTimeIntervalSinceNow:-self.config.maxDiskAge];
for (NSURL *fileURL in fileEnumerator) {
NSError *error;
// 根據(jù) url 獲取返回結(jié)果,從中獲取想要或崎嶇的屬性
NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
// Skip directories and errors.
if (error || !resourceValues || [resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
// 獲取修改時間
NSDate *modifiedDate = resourceValues[cacheContentDateKey];
// 過期了
if (expirationDate && [[modifiedDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
// 將文件總大小存儲下來
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
}
然后根據(jù)遍歷的 url 結(jié)果胚膊,遍歷每一個文件,在從每一個文件中取出我們想要的三個屬性想鹰,// 根據(jù) url 獲取返回結(jié)果,從中獲取想要或崎嶇的屬性 NSDictionary<NSString *, id> *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:&error];
這里是取出我們關(guān)心的屬性紊婉,然后接下來,判斷下辑舷,如果這個文件是目錄額話喻犁,那么就continue跳過,然后再獲取修改時間屬性何缓,
// 獲取修改時間
NSDate *modifiedDate = resourceValues[cacheContentDateKey];
// 過期了
if (expirationDate && [[modifiedDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
如果這個屬性存在肢础,并且和我們餓過期時間比較,如果文件的屬性和過期時間比較歌殃,因為過期時間是根據(jù)當(dāng)前時間算的乔妈,根據(jù)時間差算的蝙云,所以如果文件的修改時間比當(dāng)前過期時間小的話氓皱,說明過期時間的定位的那個時間點跨度沒有包含文件的修改時間,也就是說時間間隔太短了勃刨,換句話說也就是過期了波材,要不然肯定會把修改時間包含進(jìn)去,然后就把這個文件放到數(shù)組跳過身隐,
// Store a reference to this file and account for its total size.
// 將文件總大小存儲下來
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += totalAllocatedSize.unsignedIntegerValue;
cacheFiles[fileURL] = resourceValues;
緊接著計算文件的總大小廷区,后面用得到
// 將過期文件刪除
for (NSURL *fileURL in urlsToDelete) {
[self.fileManager removeItemAtURL:fileURL error:nil];
}
將上面的過期文件刪除
NSUInteger maxDiskSize = self.config.maxDiskSize;
// 如果當(dāng)前磁盤存儲文件的總大小大于配置的大小
if (maxDiskSize > 0 && currentCacheSize > maxDiskSize) {
// Target half of our maximum cache size for this cleanup pass.
const NSUInteger desiredCacheSize = maxDiskSize / 2;
// Sort the remaining cache files by their last modification time or last access time (oldest first).
// 將剩余的文件按照訪問時間進(jìn)行排序,最老的放在最前面
NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[cacheContentDateKey] compare:obj2[cacheContentDateKey]];
}];
// Delete files until we fall below our desired cache size.
for (NSURL *fileURL in sortedFiles) {
if ([self.fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
// 挨個刪除贾铝,知道大小達(dá)到我們預(yù)期的值隙轻,也就是設(shè)置的最大值的一半
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
這段代碼的意思就是取出配置設(shè)置的磁盤允許緩存的總的文件大小埠帕,因為已經(jīng)刪除過期的文件了,接下來就是刪除超出總大小的文件玖绿,這里是將剩下的文件按照時間排序敛瓷,然后遍歷剩下的文件,將最老的文件刪除斑匪,然后拿出刪除額這個文件的大小呐籽,將總大小減去這個大小和配置文件大小作比較,這里面有個策略蚀瘸,就是超出總大小之后狡蝶,刪除配置設(shè)置的總大小的一一半,也就是說每次留下配置設(shè)置的總大小緩存額一半贮勃。
// 獲取存儲的文件的總大小
- (NSUInteger)totalSize {
NSUInteger size = 0;
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
for (NSString *fileName in fileEnumerator) {
NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName];
NSDictionary<NSString *, id> *attrs = [self.fileManager attributesOfItemAtPath:filePath error:nil];
size += [attrs fileSize];
}
return size;
}
// 獲取緩存的總個數(shù)
- (NSUInteger)totalCount {
NSUInteger count = 0;
NSDirectoryEnumerator *fileEnumerator = [self.fileManager enumeratorAtPath:self.diskCachePath];
count = fileEnumerator.allObjects.count;
return count;
}
這里就是獲取文件的總大小和總數(shù)量
// 移動緩存
- (void)moveCacheDirectoryFromPath:(nonnull NSString *)srcPath toPath:(nonnull NSString *)dstPath {
NSParameterAssert(srcPath);
NSParameterAssert(dstPath);
// Check if old path is equal to new path
if ([srcPath isEqualToString:dstPath]) {
return;
}
BOOL isDirectory;
// Check if old path is directory
if (![self.fileManager fileExistsAtPath:srcPath isDirectory:&isDirectory] || !isDirectory) {
return;
}
// Check if new path is directory
if (![self.fileManager fileExistsAtPath:dstPath isDirectory:&isDirectory] || !isDirectory) {
if (!isDirectory) {
// New path is not directory, remove file
[self.fileManager removeItemAtPath:dstPath error:nil];
}
NSString *dstParentPath = [dstPath stringByDeletingLastPathComponent];
// Creates any non-existent parent directories as part of creating the directory in path
if (![self.fileManager fileExistsAtPath:dstParentPath]) {
[self.fileManager createDirectoryAtPath:dstParentPath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// New directory does not exist, rename directory
[self.fileManager moveItemAtPath:srcPath toPath:dstPath error:nil];
} else {
// New directory exist, merge the files
NSDirectoryEnumerator *dirEnumerator = [self.fileManager enumeratorAtPath:srcPath];
NSString *file;
while ((file = [dirEnumerator nextObject])) {
[self.fileManager moveItemAtPath:[srcPath stringByAppendingPathComponent:file] toPath:[dstPath stringByAppendingPathComponent:file] error:nil];
}
// Remove the old path
[self.fileManager removeItemAtPath:srcPath error:nil];
}
}
這個就是文件路徑的移動切換贪惹,也很簡單
問題總結(jié)
為什么用 nscache,和 mapTable衙猪,我的疑問是馍乙,為什么nscache和maptable一起使用,maptable是弱引用
額垫释,那么nscache和maptable都存儲一份丝格,那么nacache里面的經(jīng)過系統(tǒng)自動清理緩存之后,maptable中不也
清除了嗎?有什么用苦蒿?為什么要這樣用熙揍,用一個maptable不也能解決嗎,帶著這個問題曼尊,我在一些 ios 技術(shù)
討論群里面拋出了這個問題,最后有一個兄弟的話點醒了我脏嚷,因為可能會存在這種場景骆撇,比如我們的
nsacache和maptable都存儲一張圖片,界面上的imageview也用了這張圖片父叙,對這張圖片進(jìn)行強引用神郊,當(dāng)
nscache中的數(shù)據(jù)被清理之后,如果下次再來照這張圖片趾唱,那么如果沒有maptable二次存儲涌乳,這時候內(nèi)存緩存就不能命中了,就需要去磁盤緩存中取了甜癞,有了這個弱引用存儲夕晓,如若nacache中找不到,那么就去maptable中找悠咱,如果找到了蒸辆,就nscache再設(shè)置一次征炼,這樣就增加了命中率,提高了效率躬贡,不用去磁盤中取了柒室,說到這里,是不是豁然開朗逗宜,但是雄右,我還是有疑問,為什么不只用maptable一個數(shù)據(jù)結(jié)構(gòu)去存儲呢纺讲?為什么存儲兩份擂仍?