Skip to content

Commit dc8e2cc

Browse files
hryhoriiK97claude
andcommitted
Avoid copying text fragments and re-attributing runs when building NSAttributedString
`RCTNSAttributedStringFromAttributedString` iterated the fragments by value, copying each one (its string, its text attributes and a shadow view with three shared pointers), and attached the event emitter to a text fragment in a second `addAttributes:range:` pass over the string it had just created with its attributes, which splits and re-merges the attribute runs. Iterate by reference and put the emitter into a text fragment's initial attribute dictionary. Attachment fragments keep the existing second pass, so the attributes on every run are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 96dddb0 commit dc8e2cc

1 file changed

Lines changed: 20 additions & 8 deletions

File tree

  • packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager

‎packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
403403

404404
static NSMutableAttributedString *RCTNSAttributedStringFragmentFromFragment(
405405
const AttributedString::Fragment &fragment,
406-
UIImage *placeholderImage)
406+
UIImage *placeholderImage,
407+
NSData *_Nullable eventEmitterWrapper)
407408
{
408409
if (fragment.isAttachment()) {
409410
auto layoutMetrics = fragment.parentShadowView.layoutMetrics;
@@ -427,21 +428,32 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
427428
string = RCTNSStringFromStringApplyingTextTransform(string, textTransform);
428429
}
429430

430-
return [[NSMutableAttributedString alloc]
431-
initWithString:string
432-
attributes:RCTNSTextAttributesFromTextAttributes(fragment.textAttributes)];
431+
// The event emitter is part of the initial attributes: adding it afterwards
432+
// with `addAttributes:range:` splits and re-merges the attribute runs the
433+
// string was just created with.
434+
NSMutableDictionary<NSAttributedStringKey, id> *attributes =
435+
RCTNSTextAttributesFromTextAttributes(fragment.textAttributes);
436+
if (eventEmitterWrapper != nil) {
437+
attributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
438+
}
439+
440+
return [[NSMutableAttributedString alloc] initWithString:string attributes:attributes];
433441
}
434442
}
435443

436444
static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFromFragment(
437445
const AttributedString::Fragment &fragment,
438446
UIImage *placeholderImage)
439447
{
440-
auto nsAttributedStringFragment = RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage);
448+
NSData *eventEmitterWrapper =
449+
fragment.parentShadowView.componentHandle ? RCTWrapEventEmitter(fragment.parentShadowView.eventEmitter) : nil;
441450

442-
if (fragment.parentShadowView.componentHandle) {
443-
auto eventEmitterWrapper = RCTWrapEventEmitter(fragment.parentShadowView.eventEmitter);
451+
auto nsAttributedStringFragment =
452+
RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage, eventEmitterWrapper);
444453

454+
// A text fragment received the emitter with its initial attributes above;
455+
// an attachment's string comes from `attributedStringWithAttachment:`.
456+
if (fragment.isAttachment() && eventEmitterWrapper != nil) {
445457
NSDictionary<NSAttributedStringKey, id> *additionalTextAttributes =
446458
@{RCTAttributedStringEventEmitterKey : eventEmitterWrapper};
447459

@@ -464,7 +476,7 @@ void RCTApplyBaselineOffset(NSMutableAttributedString *attributedText)
464476

465477
[nsAttributedString beginEditing];
466478

467-
for (auto fragment : attributedString.getFragments()) {
479+
for (const auto &fragment : attributedString.getFragments()) {
468480
NSMutableAttributedString *nsAttributedStringFragment =
469481
RCTNSAttributedStringFragmentWithAttributesFromFragment(fragment, placeholderImage);
470482

0 commit comments

Comments
 (0)