At the beginning

As the saying goes that there is always a teacher in the company of three, so we have to learn more from others. So I chose YYCache, the god of open source cache framework for code research and learning. The author here just do a summary of their own and write some feelings.

Train of thought, me

YYCache consists of YYMemoryCache and YYDiskCache. YYCache encapsulates both and provides a unified interface to complete the implementation of cache.

Save the data

- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key {
    [_memoryCache setObject:object forKey:key];
    [_diskCache setObject:object forKey:key];
}
Copy the code

The first step is to store the data in a cache, with a copy cached on a physical hard drive

Take the data

- (id<NSCoding>)objectForKey:(NSString *)key { id<NSCoding> object = [_memoryCache objectForKey:key]; if (! object) { object = [_diskCache objectForKey:key]; if (object) { [_memoryCache setObject:object forKey:key]; } } return object; }Copy the code

Data is first accessed from the internal memory (because reading from memory is much faster than reading from physical disks). If it cannot be retrieved (due to limitations on the size and time of YYMemoryCache, some memory data will be cleared). At this time from the physical hard disk (in fact, the physical hard disk is also limited, so it may not be able to get), if the *** * is stored in memory at the same time (so that the next time to take, can be directly removed from the memory, the speed will greatly improve)

In fact, most of the cache implementation such as SDImageCache and other implementation ideas are such, the real difference is mainly memory cache and physical disk cache specific implementation details of the difference

YYMemoryCache

YYMemoryCache adopts the technology of combining bidirectional linked list and Dictionary, realizing the complexity of adding and deleting time is O(1). Bidirectional lists are deleted and added using MRU and LRU ideas

- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost { if (! key) return; if (! object) { [self removeObjectForKey:key]; return; } pthread_mutex_lock(&_lock); // Use pthread mutex, originally OSSpinLock, but the author changed it. But maybe because of the lack of maintenance, Os_unfair_lock _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key)); // If it has been stored before, fetch it and put it at the first of the bidirectional lists. MRU NSTimeInterval now = CACurrentMediaTime(); if (node) { _lru->_totalCost -= node->_cost; _lru->_totalCost += cost; node->_cost = cost; node->_time = now; node->_value = object; [_lru bringNodeToHead:node]; } else {node = [_YYLinkedMapNode new]; // Instantiate one and insert the header node->_cost = cost; node->_time = now; node->_key = key; node->_value = object; [_lru insertNodeAtHead:node]; } if (_lru->_totalCost > _costLimit) { dispatch_async(_queue, ^{ [self trimToCost:_costLimit]; }); } if (_lru->_totalCount > _countLimit) { _YYLinkedMapNode *node = [_lru removeTailNode]; // If the limit is exceeded, delete the last list. LRU if (_lru->_releaseAsynchronously) { dispatch_queue_t queue = _lru->_releaseOnMainThread ? dispatch_get_main_queue() : YYMemoryCacheGetReleaseQueue(); dispatch_async(queue, ^{ [node class]; });});}); } else if (_lru->_releaseOnMainThread && ! pthread_main_np()) { dispatch_async(dispatch_get_main_queue(), ^{ [node class]; //hold and release in queue }); } } pthread_mutex_unlock(&_lock); }Copy the code

YYDiskCache

YYDiskCache physical cache uses the combination of files and SQLite. When the value is lower than 20K, the physical cache is stored in SQLite, and when the value exceeds 20K, the physical cache is stored in SQLite. I am a little confused about the threshold of 20K. The author of YYCache made the conclusion by observing the implementation data. The author of YYCache made the conclusion by observing the implementation data. The lock used by the physical cache is the signal, mainly because the signal does not consume CPU

- (void)setObject:(id<NSCoding>)object forKey:(NSString *)key { if (! key) return; if (! object) { [self removeObjectForKey:key]; return; } NSData *extendedData = [YYDiskCache getExtendedDataFromObject:object]; NSData *value = nil; if (_customArchiveBlock) { value = _customArchiveBlock(object); } else { @try { value = [NSKeyedArchiver archivedDataWithRootObject:object]; } @catch (NSException *exception) { // nothing to do... } } if (! value) return; NSString *filename = nil; if (_kv.type ! If (value.length > _inlineThreshold) {// Whether the value is greater than the threshold 20K, Filename = [self _filenameForKey:key]; } } Lock(); / / signal lock [_kv saveItemWithKey: key value: the value filename: filename extendedData: extendedData]; Unlock(); }Copy the code

feeling

This paper does not have a very detailed analysis of the source code, mainly extracted some important fragments for analysis. Because YYCache overall source idea is very clear, the code is very clean, so read it doesn’t take much trouble, here mainly about ideas, detailed complete implementation recommendations to read the source code. I admire the craftsman spirit of YYKit very much. In order to write a YYCache framework, I actually investigated a lot of third-party frameworks and materials.

My blog

FlyOceanFish