{"id":47665,"date":"2025-05-09T17:17:08","date_gmt":"2025-05-09T21:17:08","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=47665"},"modified":"2025-08-05T14:28:17","modified_gmt":"2025-08-05T18:28:17","slug":"nscache-and-lrucache","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2025\/05\/09\/nscache-and-lrucache\/","title":{"rendered":"NSCache and LRUCache"},"content":{"rendered":"<p><a href=\"https:\/\/mastodon.social\/@ctietze\/114470865717534868\">Christian Tietze<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@ctietze\/114470865717534868\"><p>Is <code>NSCache<\/code> still a thing we like to use?<\/p><\/blockquote>\n<p>I&rsquo;ve <a href=\"https:\/\/x.com\/mjtsai\/status\/1423455670477770753\">discussed<\/a> this several times, but I don&rsquo;t think I ever blogged about it. The problem I have with <code>NSCache<\/code> is that its eviction strategy is not defined, and it&rsquo;s definitely not <a href=\"https:\/\/en.wikipedia.org\/wiki\/Cache_replacement_policies#LRU\">LRU<\/a>. When I tried to use <code>NSCache<\/code>, it would destroy the performance of my app because I would fetch objects from the database, put them in the cache, and then find them immediately evicted, with the cache remaining full of older objects that I didn&rsquo;t care about. I guess it could be useful as a threadsafe dictionary if you know that you won&rsquo;t be exceeding the capacity. But often when I want a cache it&rsquo;s because I need to limit the memory consumption, and in that situation <code>NSCache<\/code> just doesn&rsquo;t work for me.<\/p>\n<p>I ended up writing my own LRU cache, combining a Swift <code>Dictionary<\/code> with a linked list that tracks which keys were accessed most recently. Nick Lockwood made an open-source solution called <a href=\"https:\/\/github.com\/nicklockwood\/LRUCache\">LRUCache<\/a>, <a href=\"https:\/\/x.com\/mjtsai\/status\/1423490338665242629\">converging<\/a> on a similar design. (There were some interesting discussions in building it that have unfortunately been deleted.)<\/p>\n<p>The main thing to be aware of if you&rsquo;re writing your own is that if you let ARC deallocate your linked list nodes automatically it <a href=\"https:\/\/stackoverflow.com\/questions\/67188108\/swift-linkedlist-resulting-in-segmentation-fault\">will<\/a> <a href=\"https:\/\/forums.swift.org\/t\/singly-and-doubly-linked-list-collections-in-standard-library\/11426\">explode<\/a>. ARC uses recursion to follow the references, so if your list is too big it will <a href=\"https:\/\/forums.swift.org\/t\/arc-tail-recursion\/57054\">overflow<\/a> the stack. You can fix this by manually deleting list nodes using a loop. Or, if your list is only used by the cache, you can make the list pointers <code>unowned<\/code> and let the dictionary do the retaining.<\/p>\n\n<p><a href=\"https:\/\/sudrocket.de\/blog\/2020\/10\/how-to-make-nscache-do-its-job\/\">Matthias Maurberger<\/a>:<\/p>\n<blockquote cite=\"https:\/\/sudrocket.de\/blog\/2020\/10\/how-to-make-nscache-do-its-job\/\"><p>Recently, I needed to add caching to one of the iOS apps I&rsquo;m working on. While\nresearching a few possible ways how to go about this <a href=\"https:\/\/www.swiftbysundell.com\/articles\/caching-in-swift\/\">I came across a great\narticle written by John Sundell<\/a>\n (a pretty common occurrence when searching\nfor Swift topics on the web, thanks John!). While the basic concept he describes\nin the article worked quite well for my needs, there was one major problem with\nthe approach: <code>NSCache<\/code> will drop your objects like hot potatoes as soon as your\napp gets backgrounded.<\/p><p>[&#8230;]<\/p><p>So if you don&rsquo;t want the system to evict all objects from your cache when your\napp gets backgrounded, make sure your objects implement the\n<code>NSDiscardableContent<\/code> protocol.<\/p><\/blockquote>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2013\/12\/09\/the-foundation-collection-classes\/\">The Foundation Collection Classes<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2012\/09\/06\/migrating-from-nsmutabledictionary-to-nscache\/\">Migrating From NSMutableDictionary to NSCache<\/a><\/li>\n<\/ul>\n\n<p id=\"nscache-and-lrucache-update-2025-08-05\">Update (<a href=\"#nscache-and-lrucache-update-2025-08-05\">2025-08-05<\/a>): <a href=\"https:\/\/mastodon.social\/@nicklockwood\/114958905794517253\">Nick Lockwood<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mastodon.social\/@nicklockwood\/114958905794517253\">\n<p>I modernized my LRUCache library a bit, adding <code>Sendable<\/code> conformance and making a few other improvements[&#8230;]<\/p>\n<\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Christian Tietze: Is NSCache still a thing we like to use? I&rsquo;ve discussed this several times, but I don&rsquo;t think I ever blogged about it. The problem I have with NSCache is that its eviction strategy is not defined, and it&rsquo;s definitely not LRU. When I tried to use NSCache, it would destroy the performance [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2025-05-09T21:17:11Z","apple_news_api_id":"de1f72a3-c07b-4e4a-9921-f4b755004ca9","apple_news_api_modified_at":"2025-08-05T13:59:21Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/A3h9yo8B7TkqZIfS3VQBMqQ","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[55,69,31,2586,30,2598,571,74,71,901],"class_list":["post-47665","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-arc","tag-cocoa","tag-ios","tag-ios-18","tag-mac","tag-macos-15-sequoia","tag-memory-management","tag-opensource","tag-programming","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/47665","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=47665"}],"version-history":[{"count":3,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/47665\/revisions"}],"predecessor-version":[{"id":48777,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/47665\/revisions\/48777"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=47665"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=47665"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=47665"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}