8
8
9
9
#import < CommonCrypto/CommonDigest.h>
10
10
#import < UIKit/UIKit.h>
11
+ #import < sys/xattr.h>
11
12
12
13
#import " CYPersistenceCache.h"
13
14
@@ -24,24 +25,33 @@ - (instancetype)init {
24
25
return [self initWithNamespace: nil ];
25
26
}
26
27
27
- - (id )initWithNamespace : (NSString *)ns {
28
+ - (instancetype )initWithNamespace : (NSString *)ns {
28
29
30
+ return [self initWithNamespace: ns cachePolicy: CYPersistenceCachePolicyDefault];
31
+ }
32
+
33
+ - (instancetype )initWithNamespace : (NSString *)ns cachePolicy : (CYPersistenceCachePolicy)cachePolicy {
34
+
29
35
if (self = [super init ]) {
30
-
36
+
31
37
_cacheNamespace = ns;
32
-
38
+ _cachePolicy = cachePolicy;
39
+
33
40
if (!_cacheNamespace) {
34
-
41
+
35
42
_cacheNamespace = @" default" ;
36
43
}
37
-
44
+
38
45
NSString *fullNamespace = [NSString stringWithFormat: @" com.xiaoniuapp.cache.%@ " , _cacheNamespace];
39
46
_diskCachePath = [self makeDiskCachePath: fullNamespace];
40
47
self.totalCostLimit = 100 * 1024 * 1024 ;
48
+
49
+ [self startClearCacheAsyncIfNeeded ];
41
50
}
42
51
return self;
43
52
}
44
53
54
+ #pragma mark - save image
45
55
- (void )setImage : (UIImage *)image forKey : (NSString *)key toDisk : (BOOL )toDisk {
46
56
[self setObject: image forKey: key cost: (image.size.width * image.size.height * 8 )];
47
57
@@ -83,7 +93,7 @@ - (void)saveImageToDisk:(UIImage *)image forKey:(NSString *)key {
83
93
}
84
94
85
95
NSString *savePath = [self defaultCachePathForKey: key];
86
- NSFileManager *manager = [NSFileManager defaultManager ];
96
+ NSFileManager *manager = [CYPersistenceCache persistanceSharedManager ];
87
97
88
98
if (![manager fileExistsAtPath: _diskCachePath]) {
89
99
@@ -99,7 +109,10 @@ - (void)saveImageToDisk:(UIImage *)image forKey:(NSString *)key {
99
109
}
100
110
101
111
[manager createFileAtPath: savePath contents: imageData attributes: nil ];
102
-
112
+
113
+ // refresh last read date
114
+ [self refreshFileLastReadDate: savePath];
115
+
103
116
// 不备份
104
117
[[NSURL fileURLWithPath: savePath] setResourceValue: [NSNumber numberWithBool: YES ]
105
118
forKey: NSURLIsExcludedFromBackupKey
@@ -112,7 +125,7 @@ - (void)removeImageFromDiskForKey:(NSString *)key {
112
125
return ;
113
126
}
114
127
NSString *savePath = [self defaultCachePathForKey: key];
115
- NSFileManager *manager = [NSFileManager defaultManager ];
128
+ NSFileManager *manager = [CYPersistenceCache persistanceSharedManager ];
116
129
[manager removeItemAtPath: savePath error: NULL ];
117
130
}
118
131
@@ -121,16 +134,69 @@ - (UIImage *)imageForKeyFromDisk:(NSString *)key {
121
134
if (!key) {
122
135
return nil ;
123
136
}
124
-
137
+
125
138
NSString *savePath = [self defaultCachePathForKey: key];
126
139
NSData *data = [NSData dataWithContentsOfFile: savePath];
127
140
if (data) {
128
-
141
+
142
+ // refresh read date
143
+ [self refreshFileLastReadDate: savePath];
129
144
return [UIImage imageWithData: data];
130
145
}
131
146
return nil ;
132
147
}
133
148
149
+ static NSString *const fileLastReadDateAttributeKey = @" kCYLastReadDate" ;
150
+ - (void )refreshFileLastReadDate : (NSString *)filePath {
151
+
152
+ NSTimeInterval time = [[NSDate date ] timeIntervalSince1970 ];
153
+ const char *value = [[NSString stringWithFormat: @" %.0f " , time] UTF8String ];
154
+ setxattr ([filePath fileSystemRepresentation ],
155
+ [fileLastReadDateAttributeKey UTF8String ],
156
+ value,
157
+ strlen (value),
158
+ 0 ,
159
+ 0 );
160
+ }
161
+
162
+ - (NSDate *)fileLastReadDate : (NSString *)filePath {
163
+
164
+ const char *filePathC = [filePath fileSystemRepresentation ];
165
+ const char *attrName = [fileLastReadDateAttributeKey UTF8String ];
166
+
167
+ // get size of needed buffer
168
+ int bufferLength = getxattr (filePathC,
169
+ attrName,
170
+ NULL ,
171
+ 0 ,
172
+ 0 ,
173
+ 0 );
174
+
175
+ // make a buffer of sufficient length
176
+ char *buffer = malloc (bufferLength);
177
+
178
+ // now actually get the attribute string
179
+ getxattr (filePathC, attrName, buffer, 255 , 0 , 0 );
180
+
181
+ // convert to NSString
182
+ NSString *retString = [[NSString alloc ] initWithBytes: buffer
183
+ length: bufferLength
184
+ encoding: NSUTF8StringEncoding];
185
+
186
+ // release buffer
187
+ free (buffer);
188
+
189
+ if (retString) {
190
+
191
+ NSTimeInterval time = (NSTimeInterval )[retString doubleValue ];
192
+ if (time > 0 ) {
193
+
194
+ return [NSDate dateWithTimeIntervalSince1970: time];
195
+ }
196
+ }
197
+ return nil ;
198
+ }
199
+
134
200
#pragma mark - private save path
135
201
- (NSString *)cachePathForKey : (NSString *)key inPath : (NSString *)path {
136
202
NSString *filename = [self cachedFileNameForKey: key];
@@ -160,6 +226,76 @@ - (NSString *)makeDiskCachePath:(NSString*)fullNamespace{
160
226
return [paths[0 ] stringByAppendingPathComponent: fullNamespace];
161
227
}
162
228
229
+ #pragma mark - clear cache
230
+ static NSString *const CYPersistenceCacheLastDeleteCacheDateKey = @" CYPersistenceCacheLastDeleteCacheDateKey" ;
231
+
232
+ - (void )startClearCacheAsyncIfNeeded {
233
+
234
+ NSDate *currentDate = [NSDate date ];
235
+
236
+ NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults ];
237
+ NSDate *lastDeleteDate = [userDefaults objectForKey: CYPersistenceCacheLastDeleteCacheDateKey];
238
+
239
+ if (!lastDeleteDate) {
240
+
241
+ // if haven't an last delete date, save now
242
+ [userDefaults setObject: [NSDate date ]
243
+ forKey: CYPersistenceCacheLastDeleteCacheDateKey];
244
+ [userDefaults synchronize ];
245
+ return ;
246
+ }
247
+ // every other 30 days clear
248
+ if (lastDeleteDate
249
+ && [lastDeleteDate isKindOfClass: [NSDate class ]]
250
+ && [currentDate timeIntervalSinceDate: lastDeleteDate] > 30l * 24 * 60 * 60 ) {
251
+
252
+ [self clearCacheAsync ];
253
+ }
254
+ }
255
+
256
+ - (void )clearCacheAsync {
257
+
258
+ NSDate *currentDate = [NSDate date ];
259
+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{
260
+
261
+ NSFileManager *fileManager = [CYPersistenceCache persistanceSharedManager ];
262
+ // get file enumerator from current cache directory
263
+ NSDirectoryEnumerator <NSString *> *enumerator = [fileManager enumeratorAtPath: self .diskCachePath];
264
+
265
+ // enumerate all cached file, remove the items that the last read date more than 30 days util now
266
+ NSMutableArray *removeFile = [NSMutableArray array ];
267
+ NSString *filePath = nil ;
268
+ while ((filePath = enumerator.nextObject )) {
269
+
270
+ NSDate *lastReadDate = [self fileLastReadDate: filePath];
271
+ if (lastReadDate
272
+ && [currentDate timeIntervalSinceDate: lastReadDate] > 30l * 24 * 60 * 60 ) {
273
+
274
+ [removeFile addObject: filePath];
275
+ }
276
+ }
277
+
278
+ // remove all the items which should delete
279
+ [removeFile enumerateObjectsUsingBlock: ^(NSString *path, NSUInteger idx, BOOL * _Nonnull stop) {
280
+
281
+ [fileManager removeItemAtPath: path
282
+ error: nil ];
283
+ }];
284
+ });
285
+ }
286
+
287
+ #pragma mark - static file manager
288
+ + (NSFileManager *)persistanceSharedManager {
289
+
290
+ static NSFileManager *fileManager = nil ;
291
+ static dispatch_once_t onceToken;
292
+ dispatch_once (&onceToken, ^ {
293
+
294
+ fileManager = [[NSFileManager alloc ] init ];
295
+ });
296
+ return fileManager;
297
+ }
298
+
163
299
#pragma mark - default cache
164
300
+ (instancetype )defaultCache {
165
301
0 commit comments