Using NSCache for Automatic Memory-Based Caching in Swift
Learn how to configure and use NSCache in Swift for automatic, thread‑safe memory caching, with a worked example, limits, and verification steps.
10 Oct 2025, 11:30 UTC

When NSCache is the right tool
If you need to keep expensive objects (such as decoded images or parsed JSON) in memory only while the app is running, and you want the system to automatically discard them when memory becomes scarce, NSCache provides a thread‑safe, mutable collection that does exactly that. Unlike a plain NSDictionary, it evicts entries based on cost and count limits you define, and it does not guarantee that an object will stay until you remove it.
Setting up NSCache
Create an instance, optionally set limits, and attach a delegate if you want to be notified when objects are evicted.
import Foundation
class ImageCache {
static let shared = ImageCache()
private let cache = NSCache()
private init() {
// Example limits: total cost of 50 MB, max 100 images
cache.totalCostLimit = 50 * 1024 * 1024 // 50 MB
cache.countLimit = 100
cache.delegate = self
}
func store(_ image: UIImage, forKey key: String) {
// Approximate cost in bytes; you can use a more precise metric
let cost = image.pngData()?.count ?? 0
cache.setObject(image, forKey: key as NSString, cost: cost)
}
func image(forKey key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
}
extension ImageCache: NSCacheDelegate {
func cache(_ cache: NSCache, willEvictObject obj: Any) {
// obj is the UIImage being removed; you could log or reload it here
if let image = obj as? UIImage {
print("Cache evicted image with approximate size \(image.pngData()?.count ?? 0) bytes")
}
}
}
The example shows a singleton wrapper that stores UIImage objects with an estimated cost based on the PNG representation size. Adjust the cost calculation to match the actual memory footprint of your objects.
How cost and count limits work
totalCostLimit– the sum of thecostvalues you provide when inserting objects. When the sum exceeds this limit, the cache may automatically evict entries.countLimit– the maximum number of objects allowed, regardless of cost.- If you set either limit to 0, the cache has no restriction for that dimension.
- The system does not evict immediately when a limit is crossed; it does so lazily, often during a memory‑warning event.
Thread safety and delegate callbacks
All NSCache methods are safe to call from multiple threads without additional locking. The delegate method cache(_:willEvictObject:) is invoked on the thread that performs the eviction, which can be useful for logging or for re‑fetching data that was removed.
Limits and common pitfalls
- Not persistent: Cached data disappears when the app terminates or when the system purges the cache under memory pressure. Do not rely on it for data that must survive launches.
- Cost accuracy: If you underestimate the cost, the cache may hold more memory than you expect, delaying evictions and potentially causing memory warnings. Overestimating leads to premature evictions.
- Key mutability: NSCache uses the key’s hash and equality; mutable objects as keys can change their hash after insertion, causing missed look‑ups. Use immutable types (e.g.,
NSString) or ensure keys never change. - Delegate optional: Forgetting to set the delegate means you lose visibility into evictions, making debugging harder when objects disappear unexpectedly.
- No guarantee of retention: Even if you stay well under the limits, the system may still discard entries during extreme memory pressure. Treat the cache as a performance optimization, not a storage guarantee.
How to verify the behavior in practice
- Create a test Xcode project targeting iOS 15 or later.
- Instantiate the
ImageCache(or your own wrapper) and insert a few objects with known costs. - Print
cache.totalCostLimitandcache.countLimitbefore and after insertion to confirm the values you set. - In the iOS Simulator, choose
Debug → Simulate Memory Warning. Observe the console for the delegate’swillEvictObjectlog, indicating that objects were removed. - Run the app on a physical device, open Xcode’s Debug Gauges, and monitor the app’s memory usage while inserting many large objects; the gauge should stay near the limits you configured.
- To test thread safety, dispatch concurrent inserts and reads on multiple
DispatchQueueinstances and verify that no crashes or corrupted state occur.
These steps give you confidence that the cache behaves as described without claiming any specific test results.
Summary
Use NSCache when you need a temporary, auto‑evicting in‑memory store for expensive objects. Set realistic cost and count limits, optionally implement the delegate for eviction visibility, and remember that the cache is not a substitute for persistent storage. Verify limits and eviction behavior with simple logging and memory‑pressure simulations.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.