     
     // MARK: Clean
     
-    @objc private func cleanExpiredDiskCache() {
-        
-    }
-    
-    @objc private func backgroundCleanExpiredDiskCache() {
-        
-    }
-    
-    // MARK: Utils
+    public func cleanMemCache() {
+        memCache.removeAllObjects()
+    }
+    
+    public func clean() {
+        cleanMemCache()
+        cleanExpiredDiskCache()
+    }
+    
+    /**
+     Clean expired disk cache. This is an async operation.
+     */
+    @objc public func cleanExpiredDiskCache() {
+        cleanExpiredDiskCacheWithCompletionHander(nil)
+    }
+    
+    /**
+     This method is from Kingfisher
+     Clean expired disk cache. This is an async operation.
+     
+     - parameter completionHandler: Called after the operation completes.
+     */
+    public func cleanExpiredDiskCacheWithCompletionHander(completionHandler: (()->())?) {
+        
+        // Do things in cocurrent io queue
+        dispatch_async(ioQueue, { () -> Void in
+            
+            var (URLsToDelete, diskCacheSize, cachedFiles) = self.travelCachedFiles()
+            
+            for fileURL in URLsToDelete {
+                do {
+                    try self.fileManager.removeItemAtURL(fileURL)
+                } catch _ {
+                }
+            }
+            
+            if self.maxDiskCacheSize > 0 && diskCacheSize > self.maxDiskCacheSize {
+                let targetSize = self.maxDiskCacheSize / 2
+                
+                // Sort files by last modify date. We want to clean from the oldest files.
+                let sortedFiles = cachedFiles.keysSortedByValue {
+                    resourceValue1, resourceValue2 -> Bool in
+                    
+                    if let date1 = resourceValue1[NSURLContentModificationDateKey] as? NSDate,
+                        date2 = resourceValue2[NSURLContentModificationDateKey] as? NSDate {
+                        return date1.compare(date2) == .OrderedAscending
+                    }
+                    // Not valid date information. This should not happen. Just in case.
+                    return true
+                }
+                
+                for fileURL in sortedFiles {
+                    
+                    do {
+                        try self.fileManager.removeItemAtURL(fileURL)
+                    } catch {
+                        
+                    }
+                    
+                    URLsToDelete.append(fileURL)
+                    
+                    if let fileSize = cachedFiles[fileURL]?[NSURLTotalFileAllocatedSizeKey] as? NSNumber {
+                        diskCacheSize -= fileSize.unsignedLongValue
+                    }
+                    
+                    if diskCacheSize < targetSize {
+                        break
+                    }
+                }
+            }
+            
+            dispatch_async(dispatch_get_main_queue(), { () -> Void in
+                
+                if URLsToDelete.count != 0 {
+                    let cleanedHashes = URLsToDelete.map({ (url) -> String in
+                        return url.lastPathComponent!
+                    })
+                }
+                
+                completionHandler?()
+            })
+        })
+    }
+    
+    // MARK: Helpers
+    
+    // This method is from Kingfisher
+    
+    private func travelCachedFiles() -> (URLsToDelete: [NSURL], diskCacheSize: UInt, cachedFiles: [NSURL: [NSObject: AnyObject]]) {
+        
+        let diskCacheURL = NSURL(fileURLWithPath: cachePath)
+        let resourceKeys = [NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey]
+        let expiredDate = NSDate(timeIntervalSinceNow: -self.maxCachePeriodInSecond)
+        
+        var cachedFiles = [NSURL: [NSObject: AnyObject]]()
+        var URLsToDelete = [NSURL]()
+        var diskCacheSize: UInt = 0
+        
+        if let fileEnumerator = self.fileManager.enumeratorAtURL(diskCacheURL, includingPropertiesForKeys: resourceKeys, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles, errorHandler: nil),
+            urls = fileEnumerator.allObjects as? [NSURL] {
+            for fileURL in urls {
+                
+                do {
+                    let resourceValues = try fileURL.resourceValuesForKeys(resourceKeys)
+                    // If it is a Directory. Continue to next file URL.
+                    if let isDirectory = resourceValues[NSURLIsDirectoryKey] as? NSNumber {
+                        if isDirectory.boolValue {
+                            continue
+                        }
+                    }
+                    
+                    // If this file is expired, add it to URLsToDelete
+                    if let modificationDate = resourceValues[NSURLContentModificationDateKey] as? NSDate {
+                        if modificationDate.laterDate(expiredDate) == expiredDate {
+                            URLsToDelete.append(fileURL)
+                            continue
+                        }
+                    }
+                    
+                    if let fileSize = resourceValues[NSURLTotalFileAllocatedSizeKey] as? NSNumber {
+                        diskCacheSize += fileSize.unsignedLongValue
+                        cachedFiles[fileURL] = resourceValues
+                    }
+                } catch _ {
+                }
+            }
+        }
+        
+        return (URLsToDelete, diskCacheSize, cachedFiles)
+    }
     
     private func cachePathForKey(key: String) -> String {
         return (cachePath as NSString).stringByAppendingPathComponent(key)

public class DataCache {
    private static let cacheDirectoryPrefix = "com.nch.cache."
    private static let ioQueuePrefix = "com.nch.queue."
    
    public static var defaultCache = DataCache(name: "default")
    

        return rawKey.stringByReplacingOccurrencesOfString("/", withString: "--")
    }
}

extension Dictionary {
    func keysSortedByValue(isOrderedBefore: (Value, Value) -> Bool) -> [Key] {
        return Array(self).sort{ isOrderedBefore($0.1, $1.1) }.map{ $0.0 }
    }
}