NSCache 源码注解

752 阅读3分钟

源码来源

NSCache是Foundation 框架中用于缓存的类(内存缓存)。其使用方法类似于Dictionary。其源码实现比较简单,但是也挺坑人的尤其是totalCostLimitcountLimit属性。关键代码处我都加了注释。

//
//  Cache.swift
//  
//
//  Created by Nigel.He on 16/11/17.
//
//
//

// NSCache 中保存的实体

import Foundation
 class NSCacheEntry<KeyType : AnyObject, ObjectType : AnyObject> {
    
    var key: KeyType
    
    var value: ObjectType
    
    var cost: Int
    
    var prevByCost: NSCacheEntry?
    
    var nextByCost: NSCacheEntry?
    
    init(key: KeyType, value: ObjectType, cost: Int) {
        self.key = key
        self.value = value
        self.cost = cost
    }
}



open class NSCache<KeyType : AnyObject, ObjectType : AnyObject> : NSObject {
    
    //保存缓存的对象
    var _entries = Dictionary<UnsafeRawPointer, NSCacheEntry<KeyType, ObjectType>>()

    //同步锁,使线程安全
    private let _lock = NSLock()
    
    //当前的Cost
    private var _totalCost = 0
    
    //指向链表的头。 链表的头节点cost值最小,尾节点cost值最大
    private var _byCost: NSCacheEntry<KeyType, ObjectType>?
    
    open var name: String = ""
    
    //限制不精确,不严格
    open var totalCostLimit: Int = -1 // limits are imprecise/not strict
    
    open var countLimit: Int = -1 // limits are imprecise/not strict
    
    open var evictsObjectsWithDiscardedContent: Bool = false
    
    
    
    public override init() {}
    
    open weak var delegate: NSCacheDelegate?
    
    
    // 根据key取value
    open func object(forKey key: KeyType) -> ObjectType? {
        var object: ObjectType?
        let keyRef = unsafeBitCast(key, to: UnsafeRawPointer.self)
        _lock.lock()
        if let entry = _entries[keyRef] {
            object = entry.value
        }
        _lock.unlock()
        return object
    }
    
    
    //使用key做标示,缓存value. 默认cost=0
    open func setObject(_ obj: ObjectType, forKey key: KeyType) {
        setObject(obj, forKey: key, cost: 0)
    }
    
    
    //设置缓存,并为每个节点设置一个cost。
    open func setObject(_ obj: ObjectType, forKey key: KeyType, cost g: Int) {
        let keyRef = unsafeBitCast(key, to: UnsafeRawPointer.self)
        _lock.lock()
        _totalCost += g
        var purgeAmount = 0
        if totalCostLimit > 0 {
            //purgeAmount = (_totalCost + g) - totalCostLimit
            purgeAmount = _totalCost - totalCostLimit
        }
        
        var purgeCount = 0
        if countLimit > 0 {
            purgeCount = (_entries.count + 1) - countLimit
        }
        
        
        //这里就非常有意思了,因为只有_entries[keyRef] 存在值时才会创建链表,否则是不会创建出链表结构。
        //这里就是万恶之源呀。没有正确的链表,totalCostLimit,countLimit的行为就会受到影响。,也就导致了countLimit和totalCostLimit限制不精确,不严格。
        //我只想说,这样也可以!!!算七八糟呀!
        if let entry = _entries[keyRef] {
            entry.value = obj
            if entry.cost != g {
                entry.cost = g
                //先在链表中删除源节点,然后再加到链表中
                remove(entry)
                insert(entry)
            }
        } else {
            //这里的的代码导致了
            _entries[keyRef] = NSCacheEntry(key: key, value: obj, cost: g)
        }
        
        _lock.unlock()
        
       
        // 针对totalCostLimit,移除一些节点。 整理准确执行的先决条件是 创建出准确的链表
        var toRemove = [NSCacheEntry<KeyType, ObjectType>]()
        if purgeAmount > 0 {
            _lock.lock()
            while _totalCost - totalCostLimit > 0 {
                if let entry = _byCost {
                    _totalCost -= entry.cost
                    toRemove.append(entry)
                    remove(entry)
                } else {
                    break
                }
            }
            if countLimit > 0 {
                purgeCount = (_entries.count - toRemove.count) - countLimit
            }
            _lock.unlock()
        }
        
        //针对countLimit,移除一些节点。 整理准确执行的先决条件是 创建出准确的链表
        if purgeCount > 0 {
            _lock.lock()
            while (_entries.count - toRemove.count) - countLimit > 0 {
                if let entry = _byCost {
                    _totalCost -= entry.cost
                    toRemove.append(entry)
                    remove(entry)
                } else {
                    break
                }
            }
            _lock.unlock()
        }

        //通知代理删除了节点
        if let del = delegate {
            for entry in toRemove {
                del.cache(unsafeBitCast(self, to:NSCache<AnyObject, AnyObject>.self), willEvictObject: entry.value)
            }
        }
        _lock.lock()
        
        //在字典中移除节点
        for entry in toRemove {
            _entries.removeValue(forKey: unsafeBitCast(entry.key, to: UnsafeRawPointer.self)) // the cost list is already fixed up in the purge routines
        }
        _lock.unlock()
        
    }


    //产生双向的链表。 链表的头节点cost值最小,尾节点cost值最大
    private func insert(_ entry: NSCacheEntry<KeyType, ObjectType>) {
        if _byCost == nil {  // 第一次,初始化
            _byCost = entry
        } else {
            var element = _byCost
            while let e = element {
                if e.cost > entry.cost {
                    let newPrev = e.prevByCost
                    entry.prevByCost = newPrev
                    entry.nextByCost = e
                    break
                }
                element = e.nextByCost
            }
        }
    }
    
    //根据key, 删除一个节点
    open func removeObject(forKey key: AnyObject) {
        let keyRef = unsafeBitCast(key, to: UnsafeRawPointer.self)
        _lock.lock()
        if let entry = _entries.removeValue(forKey: keyRef) {
            _totalCost -= entry.cost
            remove(entry)
        }
        _lock.unlock()
    }
    
    //删除一个节点
    private func remove(_ entry: NSCacheEntry<KeyType, ObjectType>) {
        
        let oldPrev = entry.prevByCost
        let oldNext = entry.nextByCost
        oldPrev?.nextByCost = oldNext
        oldNext?.prevByCost = oldPrev
        
        if entry === _byCost {
            _byCost = entry.nextByCost
        }
    }
    
    //删除全部
    open func removeAllObjects() {
        _lock.lock()
        _entries.removeAll()
        _byCost = nil
        _totalCost = 0
        _lock.unlock()
    }
}



public protocol NSCacheDelegate : NSObjectProtocol {
    func cache(_ cache: NSCache<AnyObject, AnyObject>, willEvictObject obj: AnyObject)
}



extension NSCacheDelegate {
    func cache(_ cache: NSCache<AnyObject, AnyObject>, willEvictObject obj: AnyObject) {
        // Default implementation does nothing
    }
}