Browse Source

更新,增加归档到闪存功能

gaosboy 12 năm trước cách đây
mục cha
commit
f6af079454
6 tập tin đã thay đổi với 167 bổ sung20 xóa
  1. 4 1
      Kache/KConfig.h
  2. 1 0
      Kache/KHolder.h
  3. 92 7
      Kache/KHolder.m
  4. 3 3
      Kache/KObject.h
  5. 34 8
      Kache/KObject.m
  6. 33 1
      README.md

+ 4 - 1
Kache/KConfig.h

@@ -16,4 +16,7 @@
 #define     KACHE_DEFAULT_QUEUE_SIZE    10
 
 // Default expired time, 10 Days.
-#define     KACHE_DEFAULT_LIFE_DURATION 864000
+#define     KACHE_DEFAULT_LIFE_DURATION 864000
+
+// 把内存归档到磁盘的阈值,单位 byte
+#define ARCHIVING_THRESHOLD             50000

+ 1 - 0
Kache/KHolder.h

@@ -16,6 +16,7 @@
 {
     NSMutableDictionary         *objects;
     NSMutableArray              *keys;
+    NSUInteger                  size;
 }
 
 - (void)setValue:(id)value forKey:(NSString *)key expiredAfter:(NSInteger)duration;

+ 92 - 7
Kache/KHolder.m

@@ -6,6 +6,8 @@
 //  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
 //
 
+#define Kache_Objects_Disk_Path         @"Caches/Kache_objects"
+
 #import "KConfig.h"
 #import "KHolder.h"
 #import "KObject.h"
@@ -15,8 +17,15 @@
 
 @interface KHolder ()
 
-@property (strong, nonatomic)   NSMutableDictionary         *objects;
+// 正在进行归档的状态位
+@property (assign, atomic)      BOOL                        archiving;
 @property (strong, atomic)      NSMutableArray              *keys;
+@property (assign, nonatomic)   NSUInteger                  size;
+@property (strong, nonatomic)   NSMutableDictionary         *objects;
+@property (strong, nonatomic)   NSFileManager               *fileManager;
+
+// 把数据写到磁盘
+- (void)archiveData;
 
 - (void)cleanExpiredObjects;
 
@@ -24,8 +33,13 @@
 
 @implementation KHolder
 
-@synthesize objects     = _objects;
+@synthesize fileManager = _fileManager;
+// 缓存Key列表
 @synthesize keys        = _keys;
+// 缓存大小
+@synthesize size        = _size;
+// 缓存内容
+@synthesize objects     = _objects;
 
 #pragma mark - init
 
@@ -35,7 +49,8 @@
     if (self) {
         self.objects = [[NSMutableDictionary alloc] init];
         self.keys = [[NSMutableArray alloc] init];
-        
+        self.size = 0.0f;
+        self.fileManager = [NSFileManager defaultManager];
         return self;
     }
 
@@ -44,6 +59,34 @@
 
 #pragma mark - private
 
+- (void)archiveData
+{
+    self.archiving = YES;
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+	NSString *libDirectory = [paths objectAtIndex:0];
+	NSString *path = [libDirectory stringByAppendingPathComponent:Kache_Objects_Disk_Path];
+    [self.fileManager createDirectoryAtPath:path
+                              withIntermediateDirectories:YES
+                                               attributes:nil
+                                                    error:nil];
+    NSMutableArray *copiedKeys = [self.keys mutableCopy];
+    while (0 < [copiedKeys count]) {
+        // 归档至阈值一半的数据
+        if ((ARCHIVING_THRESHOLD / 2) > self.size) {
+            break;
+        }
+        NSString *key = [copiedKeys lastObject];
+        NSString *filePath = [path stringByAppendingPathComponent:key];
+        
+        NSData *data = [self.objects objectForKey:key];
+        [self.fileManager createFileAtPath:filePath contents:data attributes:nil];
+        [self.objects removeObjectForKey:key];
+        self.size -= data.length;
+        [copiedKeys removeLastObject];
+    }
+    self.archiving = NO;
+}
+
 - (void)cleanExpiredObjects
 {
     if (self.keys && 0 < [self.keys count]) {
@@ -52,7 +95,16 @@
             KObject *leftObject = [self objectForKey:tmpKey];
             if ([leftObject expiredTimestamp] < [KUtil nowTimestamp]) {
                 [self.keys removeObject:tmpKey];
-                [self.objects removeObjectForKey:tmpKey];
+                if ([[self.objects allKeys] containsObject:tmpKey]) {
+                    [self.objects removeObjectForKey:tmpKey];
+                }
+                else {
+                    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+                    NSString *libDirectory = [paths objectAtIndex:0];
+                    NSString *path = [libDirectory stringByAppendingPathComponent:Kache_Objects_Disk_Path];
+                    NSString *filePath = [path stringByAppendingPathComponent:tmpKey];
+                    [self.fileManager removeItemAtPath:filePath error:nil];
+                }
             }
             else {
                 break;
@@ -72,7 +124,18 @@
 {
     KObject *object = [[KObject alloc] initWithData:value andLifeDuration:duration];
 
-    [self.objects setValue:object.object forKey:key];
+    if (self.archiving) {
+        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+        NSString *libDirectory = [paths objectAtIndex:0];
+        NSString *path = [libDirectory stringByAppendingPathComponent:Kache_Objects_Disk_Path];
+        NSString *filePath = [path stringByAppendingPathComponent:key];
+        [self.fileManager createFileAtPath:filePath contents:object.data attributes:nil];
+    }
+    else {
+        [self.objects setValue:object.data forKey:key];
+        self.size += [object size];
+    }
+    
     KObject *suchObject = [self objectForKey:key];
 
     // TODO sort the key by expired time.
@@ -84,6 +147,7 @@
         for (int i = [self.keys count] - 1; i >= 0; i --) {
             NSString *tmpKey = [self.keys objectAtIndex:i];
             KObject *leftObject = [self objectForKey:tmpKey];
+            // 过期时间越晚
             if ([leftObject expiredTimestamp] <= [suchObject expiredTimestamp]) {
                 if (([self.keys count] - 1) == i) {
                     [self.keys addObject:key];
@@ -98,6 +162,13 @@
     if (! [self.keys containsObject:key]) {
         [self.keys insertObject:key atIndex:0];
     }
+    
+    // 超过阈值,归档
+    if ((! self.archiving)
+        && 0 < ARCHIVING_THRESHOLD
+        && ARCHIVING_THRESHOLD < self.size) {
+        [self archiveData];
+    }
 }
 
 - (id)valueForKey:(NSString *)key
@@ -113,7 +184,18 @@
 - (KObject *)objectForKey:(NSString *)key
 {
     if ([[self.objects allKeys] containsObject:key]) {
-        return [[KObject alloc] initWithDictionary:[self.objects objectForKey:key]];
+        return [[KObject alloc] initWithData:[self.objects objectForKey:key]];
+    }
+    else {
+        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+        NSString *libDirectory = [paths objectAtIndex:0];
+        NSString *path = [libDirectory stringByAppendingPathComponent:Kache_Objects_Disk_Path];
+        NSString *filePath = [path stringByAppendingPathComponent:key];
+        if ([self.fileManager fileExistsAtPath:filePath isDirectory:NO]) {
+            [self.objects setValue:[NSData dataWithContentsOfFile:filePath] forKey:key];
+            [self.fileManager removeItemAtPath:filePath error:nil];
+            return [[KObject alloc] initWithData:[self.objects objectForKey:key]];
+        }
     }
     
     return nil;
@@ -125,6 +207,7 @@
     return [NSDictionary dictionaryWithObjectsAndKeys:
             self.objects, @"objects",
             self.keys, @"keys",
+            [NSString stringWithFormat:@"%d", self.size], @"size",
             nil];
 }
 
@@ -132,9 +215,11 @@
 - (void)unserializeFrom:(NSDictionary *)dict
 {
     if ([[dict allKeys] containsObject:@"objects"]
-        && [[dict allKeys] containsObject:@"keys"]) {
+        && [[dict allKeys] containsObject:@"keys"]
+        && [[dict allKeys] containsObject:@"meta"]) {
         self.objects = [dict objectForKey:@"objects"];
         self.keys = [dict objectForKey:@"keys"];
+        self.size = [[dict objectForKey:@"size"] intValue];
     }
 }
 

+ 3 - 3
Kache/KObject.h

@@ -10,14 +10,14 @@
 
 @interface KObject : NSObject
 
-@property (strong, nonatomic)   NSMutableDictionary       *object;
-
 - (KObject *)initWithData:(id)data andLifeDuration:(NSInteger)duration;
-- (KObject *)initWithDictionary:(NSMutableDictionary *)dict;
+- (KObject *)initWithData:(NSData *)data;
 
+- (NSData *)data;
 - (id)value;
 - (NSInteger)expiredTimestamp;
 - (void)updateLifeDuration:(NSInteger)duration;
 - (BOOL)expired;
+- (NSUInteger)size;
 
 @end

+ 34 - 8
Kache/KObject.m

@@ -15,11 +15,15 @@
 
 @interface KObject ()
 
+@property (strong, nonatomic)   NSData                  *data;
+@property (strong, nonatomic)   NSMutableDictionary     *object;
+
 @end
 
 @implementation KObject
 
 @synthesize object              = _object;
+@synthesize data                = _data;
 
 #pragma mark - init
 
@@ -29,24 +33,22 @@
 
     if (self) {
         aDuration = (0 >= aDuration) ? KACHE_DEFAULT_LIFE_DURATION : aDuration;
-
-        self.object = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
-                       aData, KCH_OBJ_DATA,
-                       [NSString stringWithFormat:@"%d", [KUtil expiredTimestampForLife:aDuration]], KCH_OBJ_LIFE,
-                       nil];
-
+        self.data = [NSKeyedArchiver archivedDataWithRootObject:[[NSMutableDictionary alloc] initWithObjectsAndKeys:
+                                                     aData, KCH_OBJ_DATA,
+                                                     [NSString stringWithFormat:@"%d", [KUtil expiredTimestampForLife:aDuration]], KCH_OBJ_LIFE,
+                                                     nil]];
         return self;
     }
     
     return nil;
 }
 
-- (KObject *)initWithDictionary:(NSMutableDictionary *)dict
+- (KObject *)initWithData:(NSData *)data
 {
     self = [super init];
     
     if (self) {
-        self.object = dict;
+        self.data = data;
         return self;
     }
     
@@ -55,8 +57,16 @@
 
 #pragma - public
 
+- (NSData *)data
+{
+    return _data;
+}
+
 - (id)value
 {
+    if (nil == self.object) {
+        self.object = [NSKeyedUnarchiver unarchiveObjectWithData:self.data];
+    }
     if ([[self.object allKeys] containsObject:KCH_OBJ_DATA]
         && [self.object objectForKey:KCH_OBJ_DATA]) {
         return [self.object objectForKey:KCH_OBJ_DATA];
@@ -67,11 +77,18 @@
 
 - (NSInteger)expiredTimestamp
 {
+    if (nil == self.object) {
+        self.object = [NSKeyedUnarchiver unarchiveObjectWithData:self.data];
+    }
     return [[self.object objectForKey:KCH_OBJ_LIFE] intValue];
 }
 
 - (void)updateLifeDuration:(NSInteger)aDuration
 {
+    if (nil == self.object) {
+        self.object = [NSKeyedUnarchiver unarchiveObjectWithData:self.data];
+    }
+
     aDuration = (0 >= aDuration) ? KACHE_DEFAULT_LIFE_DURATION : aDuration;
 
     [self.object setValue:[NSString stringWithFormat:@"%d", [KUtil expiredTimestampForLife:aDuration]]
@@ -80,6 +97,10 @@
 
 - (BOOL)expired
 {
+    if (nil == self.object) {
+        self.object = [NSKeyedUnarchiver unarchiveObjectWithData:self.data];
+    }
+
     if ([KUtil nowTimestamp] < [[self.object objectForKey:KCH_OBJ_LIFE] intValue]) {
         return NO;
     }
@@ -87,4 +108,9 @@
     return YES;
 }
 
+- (NSUInteger)size
+{
+    return _data.length;
+}
+
 @end

+ 33 - 1
README.md

@@ -1,4 +1,36 @@
 kache
 =====
 
-iOS缓存控件,支持哈希,队列和时间池
+iOS缓存控件,支持哈希,队列和时间池
+
+使用方法
+=============
+import Kache的头文件 KCH.h
+
+```
+#import "KCH.h"
+```
+
+具体使用方法可参考工程中的Demo,或 iOSSF 项目:[https://github.com/gaosboy/iOSSF](https://github.com/gaosboy/iOSSF) 
+
+配置文件
+=============
+### kache.conf
+通过修改配置文件可以改变Kache控件的工作状态
+
+```
+// If it is set as 1 it use two level storage. Once more than
+// 100 objects stored in memory, the earliest objects will be
+// archived to disk storaged.
+#define     KACHE_AUTO_ARCH             0
+#define     KACHE_ARCH_THREHOLD_VALUE   500
+
+#define     KACHE_DEFAULT_POOL_SIZE     20
+#define     KACHE_DEFAULT_QUEUE_SIZE    10
+
+// Default expired time, 10 Days.
+#define     KACHE_DEFAULT_LIFE_DURATION 864000
+
+// 把内存归档到磁盘的阈值,单位 byte
+#define ARCHIVING_THRESHOLD             50000
+ ```