Conversation
javache
left a comment
There was a problem hiding this comment.
Can we do this without enable_shared_from_this, it's generally an architectural smell.
Why isn't the comparison inside RCTImageComponentView enough? Casting this_ to a void * and passing that through should avoid any stale memory access (small risk of the memory getting re-used in place)
Uthe fix is only in RCTImageComponentView now, and the reproducer still shows 0 wrong images. |
Create a new observer proxy per subscription and ignore callbacks from an older one, so a late image for a recycled view's previous source is not applied.
ca7d792 to
f3a813a
Compare
|
@javache has imported this pull request. If you are a Meta employee, you can view this in D121619393. |
Summary:
Fixes #58667.
On iOS with the new architecture, an
<Image>can show the image of another<Image>that was unmounted just before.RCTImageResponseObserverProxydispatches its callbacks to the main queue.RCTImageComponentViewused one proxy for its whole lifetime, anddidReceiveImageonly checked that the view still had a state. When a view was recycled and handed to a new<Image>while a callback for the old request was still queued, that callback was applied to the new image. If the new source was cached it had already been applied synchronously on subscribe, so the late old image stayed on screen.This change:
RCTImageResponseObserverProxyfor every subscription in_setStateAndResubscribeImageResponseObserverobserveris not the current proxy (thefromObserver:argument was already passed but not used). For failures this also stops a stale error from clearing the new image.The proxy's address identifies the subscription. The new proxy is allocated before the previous one is released, so two consecutive subscriptions never share an address. Reuse of an old address would need two resubscriptions while a callback from the first one is still queued.
Changelog:
[IOS] [FIXED] - Image no longer shows the image of a previous source after its native view is recycled
Test Plan:
Reproducer: https://github.com/mattijsf/rn-image-recycled-view-stale-image (React Native 0.87.1, also as a Snack: https://snack.expo.dev/@mattijsf/image-recycle-stale-load-ios?platform=ios). It mounts 64 uncached remote images (red), replaces them with a cached one (green) as soon as the first red one has loaded, and counts a wrong image when the new image's
onLoadreports the old image's size. A run is 50 rounds.Release build on the iOS simulator (iPhone 17 Pro Max, iOS 26.5), React Native built from source (
RCT_USE_PREBUILT_RNCORE=0), one run of 50 rounds each. The diff applies unchanged to 0.87.1.With the change the red images still show up briefly before each swap, as intended, but none of them end up on a green tile.