Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,10 @@ - (instancetype)initWithFrame:(CGRect)frame
return self;
}

// TODO: I'm not sure whether this is the correct place for cleanup
// Possibly allowing recycling and doing this in prepareForRecycle would be better
- (void)willMoveToWindow:(RNGHWindow *)newWindow
{
if (newWindow == nil) {
RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId];
react_native_assert(handlerManager != nullptr && "Tried to access a non-existent handler manager");

[handlerManager.registry cancelAllObservationsForOwner:self];

for (NSNumber *handlerTag in _attachedHandlers) {
[handlerManager.registry detachHandlerWithTag:handlerTag fromHostDetector:self];
}

[_attachedHandlers removeAllObjects];
_subscribedVirtualHandlers.clear();
[_subscribedHandlers removeAllObjects];
[_nativeHandlers removeAllObjects];
[self detachAllHandlers];
} else {
const auto &props = *std::static_pointer_cast<const RNGestureHandlerDetectorProps>(_props);
[self attachHandlers:props.handlerTags
Expand All @@ -70,6 +56,36 @@ - (void)willMoveToWindow:(RNGHWindow *)newWindow
}
}

// Detaching in `willMoveToWindow:` alone is not enough - a detector unmounted while
// its ancestor is already off-window (e.g. an inactive screen) never gets that call,
// and would enter the recycle pool still carrying recognizers of live handlers.
- (void)detachAllHandlers
{
if (_moduleId == -1) {
return;
}

RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId];
if (handlerManager == nil) {
// A nil manager for a known moduleId means the module was invalidated mid-teardown
// and there is nothing left to clean up. An unknown moduleId is a real bug.
react_native_assert(
[RNGestureHandlerModule hasModuleWithId:_moduleId] && "Tried to access a non-existent handler manager");
return;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[handlerManager.registry cancelAllObservationsForOwner:self];

for (NSNumber *handlerTag in _attachedHandlers) {
[handlerManager.registry detachHandlerWithTag:handlerTag fromHostDetector:self];
}

[_attachedHandlers removeAllObjects];
_subscribedVirtualHandlers.clear();
[_subscribedHandlers removeAllObjects];
[_nativeHandlers removeAllObjects];
}

- (void)setDefaultProps
{
static const auto defaultProps = std::make_shared<const RNGestureHandlerDetectorProps>();
Expand Down Expand Up @@ -144,6 +160,7 @@ - (void)prepareForRecycle
{
[super prepareForRecycle];

[self detachAllHandlers];
[self setDefaultProps];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
: RCTEventEmitter <NativeRNGestureHandlerModuleSpec, RCTJSDispatcherModule, RCTInitializing>

+ (RNGestureHandlerManager *)handlerManagerForModuleId:(int)moduleId;
+ (BOOL)hasModuleWithId:(int)moduleId;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ @implementation RNGestureHandlerModule {

+ (RNGestureHandlerManager *)handlerManagerForModuleId:(int)moduleId
{
return _managers[moduleId];
// Not operator[] - that would insert a null entry on a miss.
const auto it = _managers.find(moduleId);
return it == _managers.end() ? nil : it->second;
}

+ (BOOL)hasModuleWithId:(int)moduleId
{
return _managers.find(moduleId) != _managers.end();
}

RCT_EXPORT_MODULE()
Expand All @@ -63,6 +70,11 @@ + (BOOL)requiresMainQueueSetup
- (void)invalidate
{
RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId];

// Clear observations before the entry is nulled - the async drop below keeps the registry
// alive for one main-queue hop, and dropAllHandlers does not touch them.
[handlerManager.registry removeAllObservations];

dispatch_async(dispatch_get_main_queue(), ^{
[handlerManager dropAllGestureHandlers];
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ typedef void (^RNGestureHandlerReadyBlock)(RNGestureHandler *_Nonnull handler);

- (void)cancelObservationForTag:(nonnull NSNumber *)handlerTag owner:(nonnull id)owner;
- (void)cancelAllObservationsForOwner:(nonnull id)owner;
- (void)removeAllObservations;

@property (nonatomic, readonly, nonnull) NSDictionary<NSNumber *, RNGestureHandler *> *handlers;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ - (void)cancelAllObservationsForOwner:(id)owner
}
}

- (void)removeAllObservations
{
@synchronized(_handlers) {
[_observers removeAllObjects];
}
}

- (void)attachHandlerWithTag:(NSNumber *)handlerTag
toView:(RNGHUIView *)view
withActionType:(RNGestureHandlerActionType)actionType
Expand Down
Loading