iOS中NSCache緩存機制

應(yīng)用場景:

iOS中需要頻繁讀取的數(shù)據(jù),都可以用NSCache把數(shù)據(jù)緩存到內(nèi)存中提高讀取性能。

正文:

一:定義

  • NSCache是系統(tǒng)提供的一種類似于集合(NSMutableDictionary)的緩存,它與集合的不同如下:
  1. NSCache具有自動刪除的功能,以減少系統(tǒng)占用的內(nèi)存;
  2. NSCache是線程安全的,不需要加線程鎖;
  3. 鍵對象不會像 NSMutableDictionary 中那樣被復(fù)制。(鍵不需要實現(xiàn) NSCopying 協(xié)議)。

二:屬性介紹

NSCache的屬性以及方法介紹:
@property NSUInteger totalCostLimit;
設(shè)置緩存占用的內(nèi)存大小,并不是一個嚴格的限制,當(dāng)總數(shù)超過了totalCostLimit設(shè)定的值,系統(tǒng)會清除一部分緩存,直至總消耗低于totalCostLimit的值。
@property NSUInteger countLimit;
設(shè)置緩存對象的大小,這也不是一個嚴格的限制。

- (id)objectForKey:(id)key;
獲取緩存對象,基于key-value對
- (void)setObject:(id)obj forKey:(id)key; // 0 cost
存儲緩存對象,考慮緩存的限制屬性;
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g;
存儲緩存對象,cost是提前知道該緩存對象占用的字節(jié)數(shù),也會考慮緩存的限制屬性,建議直接使用  - (void)setObject:(id)obj forKey:(id)key;
NSCacheDelegate代理

三:代理屬性聲明如下

@property (assign) id<NSCacheDelegate>delegate;
實現(xiàn)了NSCacheDelegate代理的對象,在緩存對象即將被清理的時候,系統(tǒng)回調(diào)代理方法如下:
- (void)cache:(NSCache *)cache willEvictObject:(id)obj;
第一個參數(shù)是當(dāng)前緩存(NSCache),不要修改該對象;
第二個參數(shù)是當(dāng)前將要被清理的對象,如果需要存儲該對象,可以在此操作(存入Sqlite or CoreData);
該代理方法的調(diào)用會在緩存對象即將被清理的時候調(diào)用,如下場景會調(diào)用:

1. - (void)removeObjectForKey:(id)key; 手動刪除對象;
2. 緩存對象超過了NSCache的屬性限制;(countLimit 和 totalCostLimit )
3. App進入后臺會調(diào)用;
4. 系統(tǒng)發(fā)出內(nèi)存警告;

四:NSDiscardableContent協(xié)議

NSDiscardableContent是一個協(xié)議,實現(xiàn)這個協(xié)議的目的是為了讓我們的對象在不被使用時,可以將其丟棄,以讓程序占用更少的內(nèi)存。
一個NSDiscardableContent對象的生命周期依賴于一個“counter”變量。一個NSDiscardableContent對象實際是一個可清理內(nèi)存塊,這個內(nèi)存記錄了對象當(dāng)前是否被其它對象使用。如果這塊內(nèi)存正在被讀取,或者仍然被需要,則它的counter變量是大于或等于1的;當(dāng)它不再被使用時,就可以丟棄,此時counter變量將等于0。當(dāng)counter變量等于0時,如果當(dāng)前時間點內(nèi)存比較緊張的話,內(nèi)存塊就可能被丟棄。這點類似于MRC&ARC,對象內(nèi)存回收機制。

- (void)discardContentIfPossible
當(dāng)counter等于0的時候,為了丟棄這些對象,會調(diào)用這個方法。
默認情況下,NSDiscardableContent對象的counter變量初始值為1,以確保對象不會被內(nèi)存管理系統(tǒng)立即釋放。
- (BOOL)beginContentAccess    (counter++)
調(diào)用該方法,對象的counter會加1;
與beginContentAccess相對應(yīng)的是endContentAccess。如果可丟棄內(nèi)存不再被訪問時調(diào)用。其聲明如下:
- (void)endContentAccess  (counter--)
該方法會減少對象的counter變量,通常是讓對象的counter值變回為0,這樣在對象的內(nèi)容不再被需要時,就要以將其丟棄。
NSCache類提供了一個屬性,來標(biāo)識緩存是否自動舍棄那些內(nèi)存已經(jīng)被丟棄的對象(默認該屬性為YES),其聲明如下:
@property BOOL evictsObjectsWithDiscardedContent
如果設(shè)置為YES,則在對象的內(nèi)存被丟棄時舍棄對象。
個人建議:如果需要使用緩存,直接用系統(tǒng)的NSCache就OK了,不要做死。
NSCache就寫到這里了,歡迎大家來指正錯誤,我們一起進步,感謝大家的閱讀。

使用場景

一: 緩存數(shù)量限制代碼示例

代碼演練

需要實現(xiàn)NSCacheDelegate
@interface ViewController () <NSCacheDelegate>
實現(xiàn)代理方法:
// MARK: NSCache Delegate
// 當(dāng)緩存中的對象被清除的時候,會自動調(diào)用
// obj 就是要被清理的對象
// 提示:不建議平時開發(fā)時重寫!僅供調(diào)試使用
- (void)cache:(NSCache *)cache willEvictObject:(id)obj {
    [NSThread sleepForTimeInterval:0.5];
    NSLog(@"清除了-------> %@", obj);
}
聲明NSCache變量:
@property (nonatomic, strong) NSCache *cache;
懶加載:
- (NSCache *)cache {
    if (_cache == nil) {
        _cache = [[NSCache alloc] init];
        // 設(shè)置數(shù)量限制,最大限制為10
        _cache.countLimit = 10;
        _cache.delegate = self;
    }
    return _cache;
}
測試Demo:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    for (int i = 0; i < 20; ++i) {
        NSString *str = [NSString stringWithFormat:@"hello - %04d", i];

        NSLog(@"設(shè)置 %@", str);
        // 添加到緩存
        [self.cache setObject:str forKey:@(i)];
    }

    // - 查看緩存內(nèi)容,NSCache 沒有提供遍歷的方法,只支持用 key 來取值
    for (int i = 0; i < 20; ++i) {
        NSLog(@"緩存中----->%@", [self.cache objectForKey:@(i)]);
    }
}

運行結(jié)果:
2015-03-25 09:27:19.953 01-NSCache演練[26010:681046] 設(shè)置 hello - 0000
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0001
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0002
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0003
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0004
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0005
2015-03-25 09:27:19.954 01-NSCache演練[26010:681046] 設(shè)置 hello - 0006
2015-03-25 09:27:19.955 01-NSCache演練[26010:681046] 設(shè)置 hello - 0007
2015-03-25 09:27:19.955 01-NSCache演練[26010:681046] 設(shè)置 hello - 0008
2015-03-25 09:27:19.955 01-NSCache演練[26010:681046] 設(shè)置 hello - 0009
2015-03-25 09:27:19.955 01-NSCache演練[26010:681046] 設(shè)置 hello - 0010
2015-03-25 09:27:20.456 01-NSCache演練[26010:681046] 清除了-------> hello - 0000
2015-03-25 09:27:20.457 01-NSCache演練[26010:681046] 設(shè)置 hello - 0011
2015-03-25 09:27:20.957 01-NSCache演練[26010:681046] 清除了-------> hello - 0001
2015-03-25 09:27:20.957 01-NSCache演練[26010:681046] 設(shè)置 hello - 0012
2015-03-25 09:27:21.458 01-NSCache演練[26010:681046] 清除了-------> hello - 0002
2015-03-25 09:27:21.459 01-NSCache演練[26010:681046] 設(shè)置 hello - 0013
2015-03-25 09:27:21.959 01-NSCache演練[26010:681046] 清除了-------> hello - 0003
2015-03-25 09:27:21.959 01-NSCache演練[26010:681046] 設(shè)置 hello - 0014
2015-03-25 09:27:22.461 01-NSCache演練[26010:681046] 清除了-------> hello - 0004
2015-03-25 09:27:22.461 01-NSCache演練[26010:681046] 設(shè)置 hello - 0015
2015-03-25 09:27:22.962 01-NSCache演練[26010:681046] 清除了-------> hello - 0005
2015-03-25 09:27:22.962 01-NSCache演練[26010:681046] 設(shè)置 hello - 0016
2015-03-25 09:27:23.464 01-NSCache演練[26010:681046] 清除了-------> hello - 0006
2015-03-25 09:27:23.464 01-NSCache演練[26010:681046] 設(shè)置 hello - 0017
2015-03-25 09:27:23.965 01-NSCache演練[26010:681046] 清除了-------> hello - 0007
2015-03-25 09:27:23.965 01-NSCache演練[26010:681046] 設(shè)置 hello - 0018
2015-03-25 09:27:24.466 01-NSCache演練[26010:681046] 清除了-------> hello - 0008
2015-03-25 09:27:24.466 01-NSCache演練[26010:681046] 設(shè)置 hello - 0019
2015-03-25 09:27:24.967 01-NSCache演練[26010:681046] 清除了-------> hello - 0009
2015-03-25 09:27:24.967 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.967 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.968 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.968 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.968 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.968 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.969 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.969 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.969 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.969 01-NSCache演練[26010:681046] 緩存中----->(null)
2015-03-25 09:27:24.969 01-NSCache演練[26010:681046] 緩存中----->hello - 0010
2015-03-25 09:27:24.970 01-NSCache演練[26010:681046] 緩存中----->hello - 0011
2015-03-25 09:27:24.970 01-NSCache演練[26010:681046] 緩存中----->hello - 0012
2015-03-25 09:27:24.970 01-NSCache演練[26010:681046] 緩存中----->hello - 0013
2015-03-25 09:27:24.970 01-NSCache演練[26010:681046] 緩存中----->hello - 0014
2015-03-25 09:27:24.971 01-NSCache演練[26010:681046] 緩存中----->hello - 0015
2015-03-25 09:27:24.971 01-NSCache演練[26010:681046] 緩存中----->hello - 0016
2015-03-25 09:27:24.971 01-NSCache演練[26010:681046] 緩存中----->hello - 0017
2015-03-25 09:27:24.971 01-NSCache演練[26010:681046] 緩存中----->hello - 0018
2015-03-25 09:27:24.971 01-NSCache演練[26010:681046] 緩存中----->hello - 0019

總結(jié)
通過打印結(jié)果可以知道,當(dāng)超多最大成本限制的時候,會先清除緩存中的一條數(shù)據(jù),再存入一條新的數(shù)據(jù)。最后緩存中只能保存最大成本數(shù)的數(shù)據(jù),即10條。

二:關(guān)聯(lián)沙箱代碼示例:(減少頻繁讀取文件時間)

代碼示例

#import "AHPersonInfoManager.h"
#import "YYModel.h"
@interface AHPersonInfoManager()
//用戶信息模型
@property(nonatomic,strong)AHPersonInfoModel *model;
//個人信息緩存
@property(nonatomic,strong)NSCache *infoModelCache;

@end

@implementation AHPersonInfoManager

+(instancetype)manager{
    static AHPersonInfoManager *personInfoManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        @synchronized (personInfoManager) {
            personInfoManager = [[AHPersonInfoManager alloc]init];
            personInfoManager.infoModelCache = [[NSCache alloc]init];
        }
       
    });
    return personInfoManager;
}

#pragma mark 個人資料

-(AHPersonInfoModel*)getInfoModel{
    //讀取緩存
    if (_infoModelCache) {
        return [_infoModelCache objectForKey:@"infoModel"];
    }
    //讀取文件
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    //獲取完整路徑
    NSString *documentsDirectory = [paths objectAtIndex:0];
//    LOG(@"homeDirectory = %@", documentsDirectory);
    NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:PersonInfoFilePath];
    //讀出文件
    if (  [[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
        NSMutableDictionary *infoDic = [[[NSMutableDictionary alloc] initWithContentsOfFile:plistPath] mutableCopy];
        _model = [AHPersonInfoModel yy_modelWithDictionary:infoDic];
    }else{
        //防nil
        _model = [[AHPersonInfoModel alloc]init];
    }
    //寫入緩存
    [_infoModelCache setObject:_model forKey:@"infoModel"];
    return _model;
}

-(void)setInfoModel:(AHPersonInfoModel*)infoModel{
    @synchronized (self) {
        //寫入緩存
        if (!_infoModelCache) {
            _infoModelCache = [[NSCache alloc]init];
        }
        [_infoModelCache setObject:infoModel forKey:@"infoModel"];
        //寫入文件
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        //獲取完整路徑
        NSString *documentsDirectory = [paths objectAtIndex:0];
        //    LOG(@"homeDirectory = %@", documentsDirectory);
        NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:PersonInfoFilePath];
        NSDictionary *dic = [infoModel yy_modelToJSONObject];
        //寫入文件
        [dic writeToFile:plistPath atomically:YES];
        _model = infoModel;
    }

}

-(void)setWithJson:(id)json{
    @synchronized (self) {
        NSMutableDictionary *newInfoDic = [json yy_modelToJSONObject];
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        //獲取完整路徑
        NSString *documentsDirectory = [paths objectAtIndex:0];
        //    LOG(@"homeDirectory = %@", documentsDirectory);
        NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:PersonInfoFilePath];
        //原模型字典
        NSMutableDictionary *oldInfoDic= [[[NSMutableDictionary alloc] initWithContentsOfFile:plistPath] mutableCopy];
        //防nil
        if (!([oldInfoDic allKeys].count >0)) {
            oldInfoDic = [NSMutableDictionary dictionary];
        }
        
        //修改對應(yīng)的key-value
        NSArray *keyArray  = [newInfoDic allKeys];
        if (keyArray.count>0) {
            for (NSString *key in keyArray) {
                if (  [newInfoDic objectForKey:key]) {
                    //去除星座
                    if ([key isEqualToString:@"constellation"]) {
                        
                    }else{
                        [oldInfoDic setObject:newInfoDic[key] forKey:key];
                    }
                    
                }
            }
            [oldInfoDic writeToFile:plistPath atomically:YES];
            //寫入緩存
            if (!_infoModelCache) {
                _infoModelCache = [[NSCache alloc]init];
            }
            AHPersonInfoModel *infoModel = [AHPersonInfoModel yy_modelWithJSON:oldInfoDic];
            [_infoModelCache setObject:infoModel forKey:@"infoModel"];
       
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天研究SDWebimage發(fā)現(xiàn),里面使用了NSCache的一個方法setObject:forKey:cost:首...
    RealSlimAlan閱讀 2,456評論 1 6
  • 曾經(jīng)有一份美好的愛情放在我的面前我沒有珍惜。等到失去后才后悔莫及。如果可以再對小李說。毛欣想說。這輩子無緣再牽手。...
    毛欣與小李閱讀 3,365評論 0 13
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,654評論 19 139
  • 冬日的雨與不絕的寒冷, 充斥在我枯萎的日記里。 字字句句都烙印著想你, 一顆顆紅豆連綴成, 一遍遍念你的名字。 煙...
    雪嶼閱讀 220評論 0 0
  • 電腦死機需要重啟,生活也一樣。 這是開啟“21天寫作”的第一天,也是下半年開始的第一天。今天估計微博及一些公眾號主...
    MikaGO閱讀 362評論 1 0

友情鏈接更多精彩內(nèi)容