NSCache深刻淺出

介紹

  • NSCache 是蘋果提供的一個專門用來作緩存的類web

  • 使用和 NSMutableDictionary 很是類似緩存

  • 是線程安全的安全

  • 當內存不足的時候,會自動清理緩存網絡

  • 程序開始時,能夠指定緩存的數量 & 成本app

方法

  • 取值框架

    • - (id)objectForKey:(id)key;測試

  • 設置對象,0成本atom

    • - (void)setObject:(id)obj forKey:(id)key;spa

  • 設置對象並指定成本線程

    • - (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g;

  • 成本示例,以圖片爲例:

    • 方案一:緩存 100 張圖片

    • 方案二:總緩存成本設定爲 10M,以圖片的 寬 * 高看成成本,圖像像素。這樣,不管緩存的多少張照片,只要像素值超過 10M,就會自動清理

    • 結論:在緩存圖像時,使用成本,比單純設置數量要科學!

  • 刪除

    • - (void)removeObjectForKey:(id)key;

  • 刪除所有(不要使用!)

    • - (void)removeAllObjects;

屬性

  • @property NSUInteger totalCostLimit;

    • 緩存總成本

  • @property NSUInteger countLimit;

    • 緩存總數量

  • @property BOOL evictsObjectsWithDiscardedContent;

    • 是否自動清理緩存,默認是 YES

代碼演練

  • 定義緩存屬性

@property (nonatomic, strong) NSCache *cache;
  • 懶加載並設置限制

- (NSCache *)cache {    if (_cache == nil) {
        _cache = [[NSCache alloc] init];
        _cache.delegate = self;
        _cache.countLimit = 10;
    }    return _cache;
}
  • 觸摸事件添加緩存

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {   
 for (int i = 0; i < 20; ++i) { 
       
  NSString *str = [NSString stringWithFormat:@"%d", i]; 
        
   NSLog(@"set -> %@", str);
   
        [self.cache setObject:str forKey:@(i)];  
             
         NSLog(@"set -> %@ over", str);
    }   
     // 遍歷緩存
    NSLog(@"------"); 
      
     for (int i = 0; i < 20; ++i) {  
         
       NSLog(@"%@", [self.cache objectForKey:@(i)]);
    }
}// 代理方法,僅供觀察使用,開發時不建議重寫此方法
- (void)cache:(NSCache *)cache willEvictObject:(id)obj {  

  NSLog(@"remove -> %@", obj);
  
}

修改網絡圖片框架

  • 修改圖像緩衝池類型,並移動到 .h 中,以便後續測試

///  圖像緩衝池@property (nonatomic, strong) NSCache *imageCache;
  • 修改懶加載,並設置數量限制

- (NSCache *)imageCache {    if (_imageCache == nil) {
        _imageCache = [[NSCache alloc] init];
        _imageCache.countLimit = 15;
    }    return _imageCache;
}
  • 修改其餘幾處代碼,將 self.imageCache[URLString] 替換爲 [self.imageCache setObject:image forKey:URLString];

  • 測試緩存中的圖片變化

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {   
 for (AppInfo *app in self.appList) { 
       
  NSLog(@"%@ %@", [[DownloadImageManager sharedManager].imageCache objectForKey:app.icon], app.name);
    }
}
  • 註冊通知,監聽內存警告

- (instancetype)init
{
    self = [super init];
    if (self) {
        // 註冊通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    return self;
}

// 提示:雖然執行不到,可是寫了也無所謂 良好的代碼習慣


- (void)dealloc {
    // 刪除通知
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

  • 清理內存

- (void)clearMemory {
    NSLog(@"%s", __FUNCTION__);

    // 取消全部下載操做
    [self.downloadQueue cancelAllOperations];

    // 刪除緩衝池 
    [self.operationChache removeAllObjects];

}



注意:內存警告或者超出限制後,緩存中的任何對象,都有可能被清理。使用 NSCache 作緩存必定要保證可以有恢復的通道!





NSCache bug


#import "ViewController.h"

@interface ViewController ()<NSCacheDelegate>

@property(nonatomic,strong) NSCache *cache;

@end

@implementation ViewController
#pragma mark 懶加載
-(NSCache *)cache{
   
if(!_cache){
       
_cache = [[NSCache alloc] init];
       
//設置最大數量
       
_cache.countLimit = 10;
       
_cache.delegate = self;
    }
   
return _cache;
}

- (
void)viewDidLoad {
    [
super viewDidLoad];
   
// Do any additional setup after loading the view, typically from a nib.
}

-(
void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [
self demo];
}

-(
void)demo{
   
for (int i = 0; i < 20; i++) {
       
NSString *str = [NSString stringWithFormat:@"%d",i];
       
NSLog(@"add %@",str);
        [
self.cache setObject:str forKey:@(i)];
    }
   
//輸出
   
for (int i = 0; i < 20; i++) {
       
NSLog(@"%@",[self.cache objectForKey:@(i)]);
    }
}

- (void)didReceiveMemoryWarning {
   
//清空全部緩存,bug不靠譜,手動清空以後沒法再加入

    [self.cache removeAllObjects];


    [super didReceiveMemoryWarning];

}


#pragma mark delegate
-(void)cache:(NSCache *)cache willEvictObject:(id)obj{
   
//不推薦在這裏寫耗時操做
   
   
//模擬耗時操做
//    [NSThread sleepForTimeInterval:1];
   
   
//將要驅逐對象
   
NSLog(@"evict %@",obj);
}

@end

相關文章
相關標籤/搜索