Skip to content

Commit 6ea7541

Browse files
Abbondanzometa-codesync[bot]
authored andcommitted
Lazily store safe area inset state
Summary: `RCTViewComponentView` currently reserves a `UIEdgeInsets` value on every instance even though safe area observation is opt-in and rarely used. Store the last reported insets in an associated object only after a view starts emitting safe area inset changes, and clear that storage when observation stops or the view is recycled. Changelog: [Internal] Differential Revision: D124143204
1 parent 5ed9aa1 commit 6ea7541

1 file changed

Lines changed: 17 additions & 10 deletions

File tree

‎packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm‎

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,6 @@ static BOOL RCTViewIsInteractiveAccessibilityElement(UIView *view, const ViewPro
105105
}
106106
#endif
107107

108-
// Sentinel for insets that have not been set yet.
109-
static const UIEdgeInsets RCTNoSafeAreaInsetsSent = {-1, -1, -1, -1};
110-
111108
@implementation RCTViewComponentView {
112109
UIColor *_backgroundColor;
113110
CALayer *_backgroundColorLayer;
@@ -126,7 +123,6 @@ @implementation RCTViewComponentView {
126123
NSMutableSet<NSString *> *_accessibilityOrderNativeIDs;
127124
RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
128125
BOOL _focusable;
129-
UIEdgeInsets _lastSentSafeAreaInsets;
130126
}
131127

132128
#ifdef RCT_DYNAMIC_FRAMEWORKS
@@ -146,7 +142,6 @@ - (instancetype)initWithFrame:(CGRect)frame
146142
#endif
147143
_useCustomContainerView = NO;
148144
_removeClippedSubviews = NO;
149-
_lastSentSafeAreaInsets = RCTNoSafeAreaInsetsSent;
150145
}
151146
return self;
152147
}
@@ -452,7 +447,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
452447
if (newViewProps.onSafeAreaInsetsChange) {
453448
[self setNeedsLayout];
454449
} else if (oldViewProps.onSafeAreaInsetsChange) {
455-
_lastSentSafeAreaInsets = RCTNoSafeAreaInsetsSent;
450+
[self _setLastSentSafeAreaInsets:nil];
456451
}
457452

458453
// `overflow`
@@ -739,6 +734,17 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
739734

740735
#pragma mark - Safe area insets
741736

737+
- (NSValue *_Nullable)_lastSentSafeAreaInsets
738+
{
739+
return objc_getAssociatedObject(self, _cmd);
740+
}
741+
742+
- (void)_setLastSentSafeAreaInsets:(NSValue *_Nullable)lastSentSafeAreaInsets
743+
{
744+
objc_setAssociatedObject(
745+
self, @selector(_lastSentSafeAreaInsets), lastSentSafeAreaInsets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
746+
}
747+
742748
static BOOL RCTEdgeInsetsEqualWithThreshold(UIEdgeInsets lhs, UIEdgeInsets rhs, CGFloat threshold)
743749
{
744750
return ABS(lhs.left - rhs.left) <= threshold && ABS(lhs.top - rhs.top) <= threshold &&
@@ -762,12 +768,13 @@ - (void)_safeAreaInsetsMayHaveChanged
762768
}
763769

764770
UIEdgeInsets insets = self.safeAreaInsets;
765-
if (_lastSentSafeAreaInsets.top >= 0 &&
766-
RCTEdgeInsetsEqualWithThreshold(insets, _lastSentSafeAreaInsets, 1.0 / RCTScreenScale())) {
771+
NSValue *lastSentSafeAreaInsets = [self _lastSentSafeAreaInsets];
772+
if (lastSentSafeAreaInsets != nil &&
773+
RCTEdgeInsetsEqualWithThreshold(insets, lastSentSafeAreaInsets.UIEdgeInsetsValue, 1.0 / RCTScreenScale())) {
767774
return;
768775
}
769776

770-
_lastSentSafeAreaInsets = insets;
777+
[self _setLastSentSafeAreaInsets:[NSValue valueWithUIEdgeInsets:insets]];
771778

772779
static_cast<const ViewEventEmitter &>(*_eventEmitter)
773780
.onSafeAreaInsetsChange(
@@ -864,7 +871,7 @@ - (void)prepareForRecycle
864871
_filterLayer = nil;
865872
[self clearExistingBackgroundImageLayers];
866873

867-
_lastSentSafeAreaInsets = RCTNoSafeAreaInsetsSent;
874+
[self _setLastSentSafeAreaInsets:nil];
868875
_propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil;
869876
_eventEmitter.reset();
870877
_isJSResponder = NO;

0 commit comments

Comments
 (0)