API  1.0.0
CPCache.j
Go to the documentation of this file.
1 /*
2  * CPCache.j
3  * Foundation
4  *
5  * Created by William Mura.
6  * Copyright 2015, William Mura.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 
27 /*
28  * Delegate CPCacheDelegate
29  *
30  * - cache:willEvictObject: is called when a object is going to be removed
31  * When the total cost or the count exceeds the total cost limit or the count limit
32  * And also when removeObjectForKey or removeAllObjects are called
33  */
34 @protocol CPCacheDelegate <CPObject>
35 
36 @optional
37 - (void)cache:(CPCache)cache willEvictObject:(id)obj;
38 
39 @end
40 
42 
43 
58 @implementation CPCache : CPObject
59 {
60  CPDictionary _items;
61  int _currentPosition;
62  int _totalCostCache;
63  unsigned _implementedDelegateMethods;
64 
65  CPString _name;
66  int _countLimit;
67  int _totalCostLimit;
68  id <CPCacheDelegate> _delegate @accessors(property=delegate);
69 }
70 
71 
72 #pragma mark -
73 #pragma mark Initialization
74 
79 - (id)init
80 {
81  if (self = [super init])
82  {
83  _items = [[CPDictionary alloc] init];
84  _currentPosition = 0;
85  _totalCostCache = -1;
86  _implementedDelegateMethods = 0;
87 
88  _name = @"";
89  _countLimit = 0;
90  _totalCostLimit = 0;
91  _delegate = nil;
92  }
93 
94  return self;
95 }
96 
97 
98 #pragma mark -
99 #pragma mark Managing cache
100 
106 - (id)objectForKey:(id)aKey
107 {
108  return [[_items objectForKey:aKey] object];
109 }
110 
116 - (void)setObject:(id)anObject forKey:(id)aKey
117 {
118  [self setObject:anObject forKey:aKey cost:0];
119 }
120 
127 - (void)setObject:(id)anObject forKey:(id)aKey cost:(int)aCost
128 {
129  // Check if the key already exist
130  if ([_items objectForKey:aKey])
131  [self removeObjectForKey:aKey];
132 
133  // Add object
134  [_items setObject:[_CPCacheItem cacheItemWithObject:anObject cost:aCost position:++_currentPosition] forKey:aKey];
135 
136  // Invalid cost cache
137  _totalCostCache = -1;
138 
139  // Clean cache to satisfy condition (< totalCostLimit & < countLimit) if necessary
140  [self _cleanCache];
141 }
142 
147 - (void)removeObjectForKey:(id)aKey
148 {
149  // Call delegate method to warn that the object is going to be removed
150  [self _sendDelegateWillEvictObjectForKey:aKey];
151 
152  // Remove object
153  [_items removeObjectForKey:aKey];
154 
155  // Invalid cost cache
156  _totalCostCache = -1;
157 }
158 
162 - (void)removeAllObjects
163 {
164  // Call delegate method to warn that the objects are going to be removed
165  var enumerator = [_items keyEnumerator],
166  key;
167 
168  while (key = [enumerator nextObject])
169  [self _sendDelegateWillEvictObjectForKey:key]
170 
171  // Remove all objects
172  [_items removeAllObjects];
173 
174  // Invalid cost cache and reset position counter
175  _totalCostCache = -1;
176  _currentPosition = 0;
177 }
178 
179 
180 #pragma mark -
181 #pragma mark Setters
182 
188 - (void)setCountLimit:(int)aCountLimit
189 {
190  _countLimit = aCountLimit;
191  [self _cleanCache];
192 }
193 
199 - (void)setTotalCostLimit:(int)aTotalCostLimit
200 {
201  _totalCostLimit = aTotalCostLimit;
202  [self _cleanCache];
203 }
204 
209 - (void)setDelegate:(id)aDelegate
210 {
211  if (_delegate === aDelegate)
212  return;
213 
214  _delegate = aDelegate;
215  _implementedDelegateMethods = 0;
216 
217  if ([_delegate respondsToSelector:@selector(cache:willEvictObject:)])
218  _implementedDelegateMethods |= CPCacheDelegate_cache_willEvictObject_
219 }
220 
221 
222 #pragma mark -
223 #pragma mark Privates
224 
225 /*
226  * This method return the number of objects in the cache
227  */
228 - (int)_count
229 {
230  return [_items count];
231 }
232 
233 /*
234  * This method return the total cost (addition of all object's cost in the cache)
235  */
236 - (int)_totalCost
237 {
238  if (_totalCostCache >= 0)
239  return _totalCostCache;
240 
241  var enumerator = [_items objectEnumerator],
242  value;
243 
244  _totalCostCache = 0;
245 
246  while (value = [enumerator nextObject])
247  _totalCostCache += [value cost];
248 
249  return _totalCostCache;
250 }
251 
252 /*
253  * This method resequence the position of objects
254  * Otherwise the position value could rise until to cause problem
255  */
256 - (void)_resequencePosition
257 {
258  _currentPosition = 1;
259 
260  // Sort keys by position
261  var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
262  function(k1, k2)
263  {
264  return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]];
265  }
266  ];
267 
268  // Affect new positions
269  for (var i = 0; i < sortedKeys.length; ++i)
270  [[_items objectForKey:sortedKeys[i]] setPosition:_currentPosition++];
271 }
272 
273 /*
274  * Check if the totalCostLimit is exceeded
275  */
276 - (BOOL)_isTotalCostLimitExceeded
277 {
278  return ([self _totalCost] > _totalCostLimit && _totalCostLimit > 0);
279 }
280 
281 /*
282  * Check if the countLimit is exceeded
283  */
284 - (BOOL)_isCountLimitExceeded
285 {
286  return ([self _count] > _countLimit && _countLimit > 0);
287 }
288 
289 /*
290  * This method clean the cache if the totalCost or the count exceeds the totalCostLimit or the countLimit
291  * until to satisfy condition (totalCost < totalCostLimit and count < countLimit)
292  */
293 - (void)_cleanCache
294 {
295  // Check if the condition is satisfied
296  if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
297  return;
298 
299  // Sort keys by position
300  var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
301  function(k1, k2)
302  {
303  return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]];
304  }
305  ];
306 
307  // Remove oldest objects until to satisfy the break condition
308  for (var i = 0; i < sortedKeys.length; ++i)
309  {
310  if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
311  break;
312 
313  // Call delegate method to warn that the object is going to be removed
314  [self _sendDelegateWillEvictObjectForKey:sortedKeys[i]];
315 
316  // Remove object
317  [_items removeObjectForKey:sortedKeys[i]];
318 
319  // Invalid cost cache
320  _totalCostCache = -1;
321  }
322 
323  // Resequence position of all objects
324  [self _resequencePosition];
325 }
326 
327 @end
328 
329 
331 
332 - (void)_sendDelegateWillEvictObjectForKey:(id)aKey
333 {
334  if (_implementedDelegateMethods & CPCacheDelegate_cache_willEvictObject_)
335  [_delegate cache:self willEvictObject:[[_items objectForKey:aKey] object]];
336 }
337 
338 @end
339 
340 
341 /*
342  * Class _CPCacheItem
343  * Represent an item of CPCache
344  * This class allow to associate a cost and a position to an object
345  *
346  * Attributes:
347  * - object: the stored object
348  * - cost: represent the cost (of memory) of the object
349  * - position: represent the insertion order to determine the oldest object
350  */
351 @implementation _CPCacheItem : CPObject
352 {
353  CPObject _object;
354  int _cost;
355  int _position;
356 }
357 
358 + (id)cacheItemWithObject:(CPObject)anObject cost:(int)aCost position:(int)aPosition
359 {
360  var cacheItem = [[self alloc] init];
361 
362  if (cacheItem)
363  {
364  [cacheItem setObject:anObject];
365  [cacheItem setCost:aCost];
366  [cacheItem setPosition:aPosition];
367  }
368 
369  return cacheItem;
370 }
371 
372 @end
373 
375 
380 {
381  return _name;
382 }
383 
387 - (void)setName:(CPString)aValue
388 {
389  _name = aValue;
390 }
391 
395 - (int)countLimit
396 {
397  return _countLimit;
398 }
399 
403 - (void)setCountLimit:(int)aValue
404 {
405  _countLimit = aValue;
406 }
407 
411 - (int)totalCostLimit
412 {
413  return _totalCostLimit;
414 }
415 
419 - (void)setTotalCostLimit:(int)aValue
420 {
421  _totalCostLimit = aValue;
422 }
423 
424 @end
id init()
Definition: CALayer.j:126
A collection-like container with discardable objects.
Definition: CPCache.h:2
id delegate()
Definition: CALayer.j:965
void removeObjectForKey:(id aKey)
Definition: CPCache.j:147
id< CPCacheDelegate > _delegate accessors(property=delegate)
A mutable key-value pair collection.
Definition: CPDictionary.h:2
An immutable string (collection of characters).
Definition: CPString.h:2
void setObject:forKey:cost:(id anObject, [forKey] id aKey, [cost] int aCost)
Definition: CPCache.j:127
CPString name
Definition: CPException.j:47
var CPCacheDelegate_cache_willEvictObject_
Definition: CPCache.j:41