Skip to content

fix(ios): break retain cycle between renderers and RendererFactory#284

Merged
hryhoriiK97 merged 1 commit intosoftware-mansion-labs:mainfrom
mgmx-io:fix/renderer-factory-retain-cycle
May 1, 2026
Merged

fix(ios): break retain cycle between renderers and RendererFactory#284
hryhoriiK97 merged 1 commit intosoftware-mansion-labs:mainfrom
mgmx-io:fix/renderer-factory-retain-cycle

Conversation

@mgmx-io
Copy link
Copy Markdown
Contributor

@mgmx-io mgmx-io commented Apr 30, 2026

What/Why?

Closes #283

Each renderer subclass held a strong reference to RendererFactory,
which itself caches renderers in a strong-keyed dictionary. This
created a retain cycle that ARC could not break, causing the factory
and all renderers (plus their StyleConfig instances, ~9 KiB each)
to leak on every props update — significant for EnrichedMarkdownText
with frequently changing markdown props (e.g. LLM streaming).

Fix: change _rendererFactory from strong to __weak in all 15
renderer subclasses. AttributedRenderer.m remains strong since it
is the actual owner of the factory. Matches the pattern already used
in ThematicBreakRenderer.m.

Testing

Profiled with Instruments → Leaks template, Release build on iPhone 15 Pro
(iOS 26), new architecture enabled. Same reproduction flow before and after.

Before: ~150 leaks per 30s scan, dominated by RendererFactory,
all renderer subclasses, and StyleConfig.

After: 0 library leaks. Only 1–2 unrelated TextInputProps
leaks from React Native core remain.

Reproduced and verified via patch-package on a production app
using EnrichedMarkdownText for LLM streaming.

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android (no Android changes — fix is iOS-only)
  • Updated documentation/README if applicable (N/A — internal change)
  • Ran example app to verify changes

Each renderer subclass held a strong reference to RendererFactory,
which itself caches renderers in a strong-keyed dictionary. This created
a retain cycle that ARC could not break, causing the factory and all
renderers (plus their StyleConfig instances) to leak on every props
update.

The fix is to make the renderer's reference to the factory __weak,
matching the pattern already used in ThematicBreakRenderer.m.
AttributedRenderer.m remains strong since it is the actual owner of
the factory.

Verified via Instruments Leaks template before/after: previously the
top of the leak list was dominated by RendererFactory, all renderer
subclasses, and StyleConfig (~9 KiB each); after the fix, none of
these classes appear in the leak list.
Copy link
Copy Markdown
Collaborator

@hryhoriiK97 hryhoriiK97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀 @mgmx-io thank you for the fix! It will be available in tomorrow's nightly release.

@hryhoriiK97 hryhoriiK97 merged commit 7d8f347 into software-mansion-labs:main May 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Memory leak: retain cycle between renderers and RendererFactory

2 participants