[HN Gopher] Memory Efficiency in iOS: Reducing footprint and beyond
___________________________________________________________________
Memory Efficiency in iOS: Reducing footprint and beyond
Author : CharlesW
Score : 52 points
Date : 2025-07-21 15:59 UTC (7 hours ago)
(HTM) web link (antongubarenko.substack.com)
(TXT) w3m dump (antongubarenko.substack.com)
| ksec wrote:
| iOS used to be hyper memory efficient. Somewhere along the line,
| that is no longer the case. The same with App responsiveness as
| well. With Apps size increasing every year.
| Someone wrote:
| For developers, the choice used to be between a memory
| efficient application or no application at all. Nowadays,
| there's a third option "not as memory efficient as possible,
| but tomorrow instead of next month/quarter, and you won't have
| to hire and pay experts to tune things"
|
| For many developers, the choice between those three is easy.
| stalfosknight wrote:
| Gross.
| epistasis wrote:
| The idea of showing a splash screen with the last app contents
| while the app loaded was a good idea on slower hardware.
|
| Now that we have faster hardware with more memory, developers
| use frameworks which no longer load fast enough, so we have the
| splash screen with the last app state, followed by a complete
| load of up of UI from the ground up with something else.
|
| And the splash screen becomes actively deceptive.
| JamesSwift wrote:
| In many ways, images are a big reason why things naturally
| become inefficient. We are up to a 3x scale factor now and so
| just by virtue of the higher resolution display we are at 3x
| the pixels for a given image at a given display size.
|
| And apps are much more image heavy now with network/cpu speeds
| improving to support real time image loading/decoding.
| supertrope wrote:
| At least with storage bloated apps show at the top of the
| "iPhone Storage" list and are at greater risk of the user
| tapping [Delete App]. There's no UI interface highlighting
| memory hogs.
| conradev wrote:
| Load images lazily -- only when needed -- to avoid wasting memory
| upfront. AsyncImage or Kingfisher are the options.
|
| Kingfisher and AsyncImage decompress images into dirty memory,
| and images are big. They're fairly CPU and memory inefficient
| from that perspective.
|
| FastImageCache uses memory mapping and is very efficient, but
| it's 10 years old: https://github.com/path/FastImageCache
|
| If anyone wants to build a modern rkyv + FastImageCache hybrid...
| jamil7 wrote:
| Is Nuke any different? I know it's author wrote a lot about
| performance but I've never looked into it.
| conradev wrote:
| Nuke also uses a simple filesystem cache, so its the same: ht
| tps://github.com/kean/Nuke/blob/main/Sources/Nuke/Caching/...
| The framework is lean and compiles in under 2 seconds
|
| It compiles fast, though!
| ethan_smith wrote:
| Nuke (https://github.com/kean/Nuke) is a modern alternative
| that handles efficient memory management with features like
| progressive decoding and intelligent prefetching.
| conradev wrote:
| It does progressive CGImage decoding right, I do remember
| looking at that! Efficient for network to pixels. But it
| stores PNGs on disk.
| JamesSwift wrote:
| So does FastImageCache as far as I can tell? But it also
| maps it to a packed sprite sheet before writing
| JamesSwift wrote:
| The two approaches seem to serve different use cases. Mapping
| to a sprite-sheet (how FastImageCache describes itself) doesnt
| matter if images arent actually reused often (eg doom
| scrolling). But if you do tend to either reuse the same image
| or scroll up/down on the same content then yes it will come out
| ahead.
|
| Ive seen a lot of async image loaders over the years on ios and
| for the most part they were all fine. The async offload and
| pushing remote url loading to the background is the most
| important part of what was historically missing from the first
| party ios api.
| conradev wrote:
| I largely agree. The other thing that happened is that phones
| got faster: it just doesn't matter as much anymore. A slow
| disk cache is _fine_ , but if it does anything on the main
| thread: straight to jail.
|
| The article is about memory efficiency, though! In order to
| get equivalent memory behavior the buffers would need to be
| marked as purgeable (from the article) so that kernel can
| empty the cache if needed. Not sure if any of these libraries
| do that.
|
| The main situation where cache speed is tested is app launch:
| load every image for the current viewport simultaneously. It
| can take some time if your images are slow to decode.
| saagarjha wrote:
| I feel like there's this misconception that mapping data from
| disk is free (this is also mentioned in the README you linked).
| It's not. If the pages are clean then you do get the benefit of
| being able to discard them when pressure is high, but if you
| need them again they have to be paged in. And for most apps
| there is no sharing of this data, so loading it will be
| accounted for against your process rather than being amortized
| across several like system libraries might be. Sure, it's
| definitely better than dirty anonymous pages, but it's not
| _free_.
| frumplestlatz wrote:
| Back on the iPhone 3G, PNG decoding alone was slow enough that
| I had to cache pre-rendered bitmap representations into SQLite
| blobs to achieve "glassy" scrolling of image-heavy grids; I'd
| do the SQLite loading on a worker thread and dump the pre-
| rendered bitmap representation right back into a CGImage
| wrapper. I recall there being some inline NEON assembly
| involved as well.
|
| Within 1-2 years, those optimizations were totally unnecessary.
| nfriedly wrote:
| I've noticed that iOS 18+ seems to issue low-memory warnings more
| frequently than older iOS versions (at least on devices with
| <=4GB RAM). It can now happen with the app is using less than
| half the device's RAM, which I don't recall seeing before. I
| assume this is because the OS is using more RAM for AI and such.
| heliographe wrote:
| Yes, that matches my observations. I develop photography
| software and in early versions of iOS 18, sometimes saving a
| large image to the camera roll would fail because the daemon in
| charge of carrying out the photo library transactions would get
| killed due to low memory. This only happened on devices that
| ran Apple intelligence. Fortunately they seemed to have fixed
| that bug around 18.1 or 18.2 if I recall.
| tomovo wrote:
| > This dramatically reduces memory usage for lists or grids with
| many items
|
| I'd love to see a specific example with before/after numbers.
|
| > Lottie [...] ends up loading every frame as a raw bitmap.
|
| Isn't Lottie rendering the vector data directly to screen?
| JamesSwift wrote:
| > Isn't Lottie rendering the vector data directly to screen?
|
| It has to rasterize if theres not native vector support, unless
| its converting the vector to GL calls. Maybe it targets lowest
| common denominator for the platforms it supports in order to
| have a stable output.
|
| iOS also didnt support native vector display until fairly
| recently, and Im not sure if its limited to baked assets in an
| image atlas or not. It also had limitations in that it only
| allowed pdf vectors rather than SVG directly. I haven't kept up
| to know what current state is.
| potatolicious wrote:
| A key tip that wasn't in the blog post: be extremely judicious
| about taking on new code dependencies.
|
| For many apps the main source of memory usage isn't stack or heap
| memory consumed during runtime, it's the loading of the binary
| itself into memory. I've seen some _wild_ app binary sizes
| (200MB+ for relatively modest apps without _that_ much
| functionality).
|
| One thing that's endlessly frustrating about mobile dev is that
| the vast majority of devs are thoughtless about dependencies, how
| they are built/linked, and how that impacts memory use. Far and
| away the dominant mode of dependencies in mobile-land is just
| "statically link it and forget about it".
|
| This is the _singular biggest contributor to app size bloat_ ,
| and pretty up there for runtime memory consumption as well.
|
| A double whammy here is that modern iOS apps are actually
| multiple binaries (main app, watch extensions, widget extensions,
| etc.), and if you heavily use static linking you're carrying
| _multiple_ copies of the bloat.
|
| A few actionable things:
|
| - Be very, very judicious about taking on new dependencies. Is a
| library offering enough value and marginal functionality to be
| worth the weight? (e.g., I don't think AFNetworking in 2025 meets
| the bar for the vast majority of people, but yet it's still
| everywhere?)
|
| - Dynamically link, especially if you're a complex app with
| multiple targets.
|
| - Deadstrip aggressively. I cannot emphasize this enough. The
| default compiler settings _will not deadstrip unused public
| symbols in static libraries_. Fix this.
___________________________________________________________________
(page generated 2025-07-21 23:01 UTC)