Skip to content

[iOS] Honor cache: 'reload' for bundled images so they don't stay in the system image cache - #59454

Open
isaaclins wants to merge 2 commits into
react:mainfrom
isaaclins:fix-ios-local-image-cache
Open

isaaclins wants to merge 2 commits into
react:mainfrom
isaaclins:fix-ios-local-image-cache

Conversation

@isaaclins

Copy link
Copy Markdown

Summary:

Fixes #12220.

Bundled images (require('./photo.jpg')) are loaded on iOS through +[UIImage imageNamed:]. UIKit keeps every image loaded that way in its own cache: after the <Image> changes source or unmounts, the decoded bitmap stays in memory. Slideshows, onboarding flows or image sequences with many large local images grow until the app is killed.

Reproduced on 0.88.0 (Release, iOS 27.1 simulator): 20 local 3000x4000 JPEGs shown one after another, then the <Image> unmounted.

while cycling after unmount
today grows ~46 MB per image, 935 MB 935 MB
with cache: 'reload' (this PR) 60–110 MB 17 MB

Two simulated memory warnings while the 935 MB were held freed nothing (the app logged Received memory warning), so this isn't a cache that empties itself under pressure.

RN's own RCTImageCache can't replace the system cache here: the bundle asset loaders opt out of it (shouldCacheLoadedImages returns NO), and it only stores images decoded from data. So simply dropping imageNamed: for everyone would leave small bundled icons uncached. Following the discussion in the issue (no heuristics, let the developer choose per source), this PR makes the existing cache: 'reload' option ("no existing cache data should be used") apply to bundled images too:

  • RCTImageURLLoader gets an optional loadImageIgnoringCacheForURL:… method. RCTImageLoader calls it when the request's cache policy is NSURLRequestReloadIgnoringLocalCacheData and the loader implements it; otherwise nothing changes.
  • RCTBundleAssetImageLoader and RCTLocalAssetImageLoader implement it with the new RCTImageFromLocalAssetURLIgnoringSystemCache, which reads the file with imageWithContentsOfFile: instead of imageNamed: (the existing file-system fallback). Images compiled into an asset catalog can only be loaded by name and still go through the system cache.
  • Default behavior is unchanged.

Usage:

<Image source={{...Image.resolveAssetSource(require('./slide.jpg')), cache: 'reload'}} />

Changelog:

[IOS] [FIXED] - Bundled images with cache: 'reload' are no longer kept in the system image cache after they stop being shown

Test Plan:

Test app: RN 0.88.0, Release build, iOS 27.1 simulator, patched React Native built from source. 20 local 3000x4000 JPEGs, each shown for 1 s, then the <Image> unmounted; app footprint sampled every second with footprint.

  • Without cache: identical to before the change (grows to 935 MB, stays after unmount).
  • With cache: 'reload': 60–110 MB while cycling, 17 MB after unmount.
  • Screenshots at image 8, image 19 and after unmount look identical in both modes.

…tem image cache

Bundled images are loaded with +[UIImage imageNamed:], whose system cache keeps
every image ever shown, even after it is unmounted and across memory warnings.
When the image source sets cache: 'reload', the bundle asset loaders now read
the file directly instead. Default behavior is unchanged.

Fixes react#12220
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 24, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 24, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memory leak when programmatically changing 'source' prop of Image

1 participant