iOS標準庫中常用資料結構和演算法之cache

歐陽大哥2013發表於2019-05-05

上一篇:iOS標準庫中常用資料結構和演算法之記憶體池

?快取Cache

快取是以鍵值對的形式進行資料的儲存和檢索,內部採用雜湊表實現。當系統出現記憶體壓力時則會釋放掉部分快取的鍵值對。 iOS系統提供了一套基於OC語言的高階快取庫NSCache,同時也提供一套基於C語言實現的快取庫libcache.dylib,本文主要介紹基於C語言的快取庫的各種API函式。

標頭檔案: #include <cache.h>, #include <cache_callbacks.h>

平臺: iOS系統

一、快取物件的建立和關閉

功能:建立或者銷燬一個快取物件。

函式簽名

int cache_create(const char *name, cache_attributes_t *attrs, cache_t **cache_out);
int cache_destroy(cache_t *cache);
複製程式碼

引數

name:[in] 建立快取時用來指定快取的字串名稱,不能為空。

attrs: [in] 設定快取的屬性。不能為空。

cache_out: [out] 返回建立的快取物件。

return: [out] 成功操作返回0,否則返回非0

描述:

快取物件是一個容器物件,其快取的內容是一個個鍵值對,至於這些鍵值對是什麼型別的資料,如何控制鍵值對的資料的生命週期,如何判斷兩個鍵是否是相同的鍵等等這些資訊快取物件本身是無法得知,因此需要我們明確的告訴快取物件如何去操作這些鍵值資訊。這也就是為什麼在建立快取物件時需要指定屬性這個引數了。屬性的引數型別是一個cache_attributes_t結構體。這個結構體的大部分資料成員都是函式指標,這些函式指標就是用來實現對鍵值進行操作的各種策略。

struct cache_attributes_s {
    uint32_t version;  //快取物件的版本資訊
    cache_key_hash_cb_t key_hash_cb;  //對鍵執行hash計算的函式,不能為空                         
    cache_key_is_equal_cb_t key_is_equal_cb;  //判斷兩個鍵是否相等的函式,不能為空                      
    
    cache_key_retain_cb_t  key_retain_cb;   //鍵加入快取時呼叫,用於增加鍵的引用計數或者進行記憶體拷貝。
    cache_release_cb_t key_release_cb;  //鍵的釋放處理函式,用於對鍵的記憶體管理使用。
    cache_release_cb_t value_release_cb;  //值的釋放處理函式,用於對值的記憶體管理使用。                         
    
    cache_value_make_nonpurgeable_cb_t value_make_nonpurgeable_cb;   //對值記憶體的非purgeable處理函式。
    cache_value_make_purgeable_cb_t value_make_purgeable_cb;  //對值的purgeable的處理函式。       
    
    void *user_data;  //附加資料,這個附加資料會在所有的這些回撥函式中出現。

	// Added in CACHE_ATTRIBUTES_VERSION_2
	cache_value_retain_cb_t value_retain_cb;   //值增加引用計數的函式,用於對值的記憶體管理使用。
};
typedef struct cache_attributes_s cache_attributes_t;

複製程式碼

上述的各種回撥函式的格式都在cache.h中有明確的定義,因此這裡就不再展開介紹了。一般情況下我們通常都會將字串或者整數來作為鍵使用,因此當你採用字串或者整數作為鍵時,系統預置了一系列快取物件屬性的預設實現函式。這些函式的宣告在cache_callbacks.h檔案中

/*
 * Pre-defined callback functions.
 */

//用於鍵進行雜湊計算的預置函式
CACHE_PUBLIC_API uintptr_t cache_key_hash_cb_cstring(void *key, void *unused);
CACHE_PUBLIC_API uintptr_t cache_key_hash_cb_integer(void *key, void *unused);
//用於鍵進行相等比較的預置函式
CACHE_PUBLIC_API bool cache_key_is_equal_cb_cstring(void *key1, void *key2, void *unused);
CACHE_PUBLIC_API bool cache_key_is_equal_cb_integer(void *key1, void *key2, void *unused);

//鍵值進行釋放的函式,這函式預設實現就是呼叫free函式,因此如果採用這個函式進行釋放處理則鍵值需要從堆中進行記憶體分配。
CACHE_PUBLIC_API void cache_release_cb_free(void *key_or_value, void *unused);

 //對值進行purgeable處理的預置函式。
CACHE_PUBLIC_API void cache_value_make_purgeable_cb(void *value, void *unused);
CACHE_PUBLIC_API bool cache_value_make_nonpurgeable_cb(void *value, void *unused);

複製程式碼

示例程式碼

//下面程式碼用於建立一個以字串為鍵的快取物件,其中的快取物件的屬性中的各個成員函式採用的是系統預設預定的函式。
 #include <cache.h>
 #include <cache_callbcaks.h>

 cache_t *im_cache;
 cache_attributes_t attrs = {
         .version = CACHE_ATTRIBUTES_VERSION_2,
         .key_hash_cb = cache_key_hash_cb_cstring,
         .key_is_equal_cb = cache_key_is_equal_cb_cstring,
         .key_retain_cb = my_copy_string,
         .key_release_cb = cache_release_cb_free,
         .value_release_cb = cache_release_cb_free,
  };
  cache_create("com.acme.im_cache", &attrs, &im_cache);
複製程式碼

二、快取物件中鍵值對的設定和獲取以及刪除

功能:用於處理鍵值對在快取中的新增、獲取和刪除操作。

函式簽名:

//將鍵值對新增到快取,或者替換掉原有的鍵值對。
 int cache_set_and_retain(cache_t *cache, void *key, void *value, size_t cost);

//從快取中根據鍵獲取值
int cache_get_and_retain(cache_t *cache, void *key, void **value_out);

//釋放快取中的值
int cache_release_value(cache_t *cache, void *value);

//從快取中刪除鍵值。
int cache_remove(cache_t *cache, void *key);

複製程式碼

引數

cache:[in] 快取物件。

key:[in] 新增或者獲取或者刪除時的鍵。

cost:[in] 新增快取時的成本代價,值越大鍵值在快取中保留的時間就越長久。

value:[in] 新增時的值。

value_out: [out] 用於值獲取時的輸出。

描述

  1. cache_set_and_retain 函式用於將鍵值對放入快取中,並指定cost值。當將一個鍵新增到快取時,系統內部分別會呼叫快取屬性cache_attributes_t結構體中的key_retain_cb來實現對鍵的記憶體的管理,如果這個函式設定為NULL的話那就表明我們需要自己負責鍵的生命週期的管理。因為快取物件內部是通過雜湊表來進行資料的儲存和檢索的,所以在將鍵值對加入快取時,還需要提供對鍵進雜湊計算和比較的屬性函式key_hash_cb,key_is_equal_cb。 而對於值來說,當值加入快取時系統會將值的引用計數設定為1,如果我們想自定義值的引用計數時則需要指定快取屬性中的value_retain_cb來實現,加入快取中的值是可以為NULL的。最後的cost引數用於指定這個鍵值對的成本值,值越小在快取中保留的時間就越少,反之亦然。

  2. cache_get_and_retain函式用來根據鍵獲取對應的值,如果快取中沒有儲存對應的鍵值對,則value_out返回NULL,並且函式返回特殊的值ENOENT。每呼叫一次值的獲取,快取物件都會增加值的引用計數。因此當我們不再需要訪問返回的值時則需要呼叫手動呼叫cache_release_value函式來減少快取物件中值的引用計數。而當值的引用計數變為0時則快取物件會呼叫屬性結構中的value_release_cb函式來實現值的銷燬和釋放處理。

  3. cache_remove函式用於刪除快取中的鍵值對。當刪除快取中的鍵值對時,快取物件會呼叫屬性結構體中的key_release_cb和value_release_cb函式來進行引用計數管理,從而刪除對應的鍵值對。

示例程式碼:

#include <cache.h>
#include <cache_callbacks.h>

void main()
{
    cache_attributes_t attr;
    attr.key_hash_cb = cache_key_hash_cb_cstring;
    attr.key_is_equal_cb = cache_key_is_equal_cb_cstring;
    attr.key_retain_cb =  NULL;
    attr.key_release_cb = cache_release_cb_free;
    attr.version = CACHE_ATTRIBUTES_VERSION_2;
    attr.user_data = NULL;
    attr.value_retain_cb = NULL;
    attr.value_release_cb = cache_release_cb_free;
    attr.value_make_purgeable_cb = cache_value_make_purgeable_cb;
    attr.value_make_nonpurgeable_cb = cache_value_make_nonpurgeable_cb;
    
    //建立快取
    cache_t *cache = NULL;
    int ret = cache_create("com.test", &attr, &cache);
    
    //將鍵值對放入快取
    char *key = malloc(4);
    strcpy(key, "key");
    char *val = malloc(4);
    strcpy(val, "val");
    ret = cache_set_and_retain(cache, key, val, 0);
    
    //獲取鍵值對,使用後要釋放。
    char *val2 = NULL;
    ret = cache_get_and_retain(cache, key, (void**)&val2);
    ret = cache_release_value(cache, val2);
    
    //刪除鍵值
    cache_remove(cache, key);
    
    //銷燬快取。
    cache_destroy(cache);
}
複製程式碼

歡迎大家訪問歐陽大哥2013的github地址簡書地址

相關文章