4 ms·
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
by potatolicious 1y ago
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.
- vkoskiv 1y ago> 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. Does iOS not do demand paging?
- potatolicious 1y agoAFAIK not for the program itself. Plenty of ability to page for other runtime memory consumption. I can only assume this is done with a presumption that responsiveness is a greater priority than the ability to accommodate extremely large binaries and suffer cache thrash. While the process is active the whole binary is loaded (note: may not include dynamically linked libs, which is why that's a good idea - dylibs can be configured to load on-demand). A triply-good reason why the pattern of "giant ball of statically linked things" is an anti-pattern.