YYCache源碼解析筆記(一)

YYKVStorageItem文件

先看頭文件

@interface YYKVStorageItem : NSObject
@property (nonatomic, strong) NSString *key;        ///< key
@property (nonatomic, strong) NSData *value;        ///< value
@property (nonatomic, strong) NSString *filename;   ///< filename (nil if inline)
@property (nonatomic, assign) int size;             ///< value's size in bytes
@property (nonatomic, assign) int modTime;          ///< 最后修改時間戳
@property (nonatomic, assign) int accessTime;       ///< 最后訪問時間戳
@property (nonatomic, strong) NSData *extendedData; ///< extended data (nil if no extended data)
@end

YYKVStorageItem指的是每一個存儲的單元,也可以理解為某一個鍵值對,只是這個存儲單元的屬性更加完善,還有修改時間戳和訪問時間戳,以及補充的數據,這個補充的數據有什么意義呢,我的理解是補充說明這個存儲單元。

YYKVStorage文件

typedef NS_ENUM(NSUInteger, YYKVStorageType) {
    
    /// The `value` is stored as a file in file system.
    YYKVStorageTypeFile = 0,
    
    /// The `value` is stored in sqlite with blob type.
    YYKVStorageTypeSQLite = 1,
    
    /// The `value` is stored in file system or sqlite based on your choice.
    YYKVStorageTypeMixed = 2,
};

關于YYKVStorageType,作者分析了YYKVStorageTypeSQLite,YYKVStorageTypeFile這兩種存儲方式的效率問題,得出了這樣的結論:如果你想儲存大量的比較小的數據(如聯系人緩存),使用YYKVStorageTypeSQLite來獲得更好的性能。如果你想存儲大文件(如圖像緩存),使用YYKVStorageTypeFile來獲得更好的性能。當然也可以使用YYKVStorageTypeMixed為每個項目選擇存儲類型。

YYKVStorage和YYKVStorageItem的關系

YYKVStorage像一個manager用來管理每一個YYKVStorageItem,我們可以通過YYKVStorage來操作單個的YYKVStorageItem。比如保存,移除,獲取等等。大家可以對照YYKVStorage文件,里面有非常多的相關操作函數。

保存一個緩存項
- (BOOL)saveItemWithKey:(NSString *)key value:(NSData *)value filename:(NSString *)filename extendedData:(NSData *)extendedData {
    if (key.length == 0 || value.length == 0) return NO;
    if (_type == YYKVStorageTypeFile && filename.length == 0) {
        return NO;
    }
    
    if (filename.length) {
        if (![self _fileWriteWithName:filename data:value]) {
            return NO;
        }
        if (![self _dbSaveWithKey:key value:value fileName:filename extendedData:extendedData]) {
            [self _fileDeleteWithName:filename];
            return NO;
        }
        return YES;
    } else {
        if (_type != YYKVStorageTypeSQLite) {
            NSString *filename = [self _dbGetFilenameWithKey:key];
            if (filename) {
                [self _fileDeleteWithName:filename];
            }
        }
        return [self _dbSaveWithKey:key value:value fileName:nil extendedData:extendedData];
    }
}

保存一個緩存項的操作步驟:
1.先判斷key,value是否為空
2.filename不為空的情況下,先將NSData寫入磁盤文件,然后向數據庫插入一條磁盤緩存文件的表記錄
filename為空的情況下,根據key查詢表中記錄緩存項的filename字段,刪除查詢到的filename對
應的緩存文件,更新key對應的緩存表記錄的filename字段,清空為nil。

刪除多個緩存項
- (BOOL)removeItemForKeys:(NSArray *)keys {
  if (keys.count == 0) return NO;
  switch (_type) {
      case YYKVStorageTypeSQLite: {
          return [self _dbDeleteItemWithKeys:keys];
      } break;
      case YYKVStorageTypeFile:
      case YYKVStorageTypeMixed: {
          NSArray *filenames = [self _dbGetFilenameWithKeys:keys];
          for (NSString *filename in filenames) {
              [self _fileDeleteWithName:filename];
          }
          return [self _dbDeleteItemWithKeys:keys];
      } break;
      default: return NO;
  }
}

刪除多個緩存項的操作步驟:
1.先判斷數組是否為空
2.判斷緩存方式是哪一種
如果是sqlite存儲,刪除sqlite里對應的緩存項
如果是文件存儲,先獲取所有文件名,然后刪除
如果是混合模式,重復上述步驟

刪除所有項
- (BOOL)removeAllItems {
    if (![self _dbClose]) return NO;
    [self _reset];
    if (![self _dbOpen]) return NO;
    if (![self _dbInitialize]) return NO;
    return YES;
}

刪除所有目錄的操作步驟是:
1.首先關閉數據庫,并確認已經關閉
2.格式化數據庫,移除db.sqlite,db.sqlite-shm,db.sqlite-wal文件(這三個文件具體是什么職責后面詳細介紹),將所有磁盤文件移動到Trash目錄下,然后將刪除Trash目錄下文件的操作放到子線程隊列上異步執行
3.初始化數據庫

補充:在沙盒下的SQLite數據庫包含三個文件:db.sqlite,db.sqlite-shm,db.sqlite-wal。
db.sqlite 存放數據庫本身的數據: 表、表記錄、index、sequence …
db.sqlite-shm 存放的是Shared-Memory Files,數據庫必須開啟WAL模式,當多個線程/進程訪問同一個數據庫時,讓每一個數據庫連接可以進行內存數據共享,隨著 db-wal文件創建而創建,刪除而刪除.
db.sqlite-wal Write-Ahead Log (WAL) Files WAL的全稱是Write Ahead Logging,它是很多數據庫中用于實現原子事務的一種機制。同樣數據庫必須開啟WAL模式,用于當數據庫實現實現原子事務,當創建第一個數據庫連接時創建 db-wal文件,當最后一個數據庫連接關閉時刪除 db-wal文件.

查詢某個項
- (YYKVStorageItem *)getItemForKey:(NSString *)key {
    if (key.length == 0) return nil;
    YYKVStorageItem *item = [self _dbGetItemWithKey:key excludeInlineData:NO];
    if (item) {
        [self _dbUpdateAccessTimeWithKey:key];
        if (item.filename) {
            item.value = [self _fileReadWithName:item.filename];
            if (!item.value) {
                [self _dbDeleteItemWithKey:key];
                item = nil;
            }
        }
    }
    return item;
}

查詢的步驟是:
1.先判斷key是否為空
2.通過Key查找到對應的緩存項
3.如果查找成功,更新訪問時間,然后通過文件名,讀取數據,如果數據為空,則通過key刪除對應的文件

查詢多個項
- (NSArray *)getItemForKeys:(NSArray *)keys {
    if (keys.count == 0) return nil;
    NSMutableArray *items = [self _dbGetItemWithKeys:keys excludeInlineData:NO];
    if (_type != YYKVStorageTypeSQLite) {
        for (NSInteger i = 0, max = items.count; i < max; i++) {
            YYKVStorageItem *item = items[i];
            if (item.filename) {
                item.value = [self _fileReadWithName:item.filename];
                if (!item.value) {
                    if (item.key) [self _dbDeleteItemWithKey:item.key];
                    [items removeObjectAtIndex:i];
                    i--;
                    max--;
                }
            }
        }
    }
    if (items.count > 0) {
        [self _dbUpdateAccessTimeWithKeys:keys];
    }
    return items.count ? items : nil;
}

查詢多個緩存項步驟:
1.先判斷需要查找的緩存數組是否為空
2.然后在數據庫里拿到所有緩存項
3.遍歷出每個緩存項,并確保值存在,不存在的刪除掉
4.最后更新所有緩存項的訪問時間。

微博賬號:梅嘉慶(點擊關注)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容