From 93ded152d9e6c645b7dfb9e649958ef898d7edaa Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 1 Nov 2019 09:35:40 -0700 Subject: [PATCH] Fabric: Introducing `RCTComponentViewDescriptor` Summary: This diff introduces RCTComponentViewDescriptor - the container for a view and associated with this view properties which mounting infra uses for bookkeeping (and recycling) views. The previous implementation used raw Objective-C pointers to `UIView`s to store and manipulate them. The new way has a bunch of advantages: * It allows using high-performant C++ data collections for storing views and their properties (in future diffs). * The new approach allows us to avoid hacks around NSMapTable (such as ` [_registry setObject:componentView forKey:(__bridge id)(void *)tag];`) that were needed because NSMapTable wasn't designed for our use-case. * Dealing with `RCTComponentViewDescriptor` which stores a pointer to a view sometimes is actually more efficient than dealing with those pointers themselfs because we can deal with `const &` to a descriptor which does not require a ref-counter bump. * A new approach is much more flexible, it allows us to store additional data alongside view instances which we will use in coming diffs. Changelog: [Internal] Fabric-specific internal change. Reviewed By: sammy-SC Differential Revision: D18217102 fbshipit-source-id: 063e6c7df794a2e1fd690c194fb31ad6833eaba7 --- .../Mounting/RCTComponentViewDescriptor.h | 27 ++++++ .../Fabric/Mounting/RCTComponentViewFactory.h | 4 +- .../Mounting/RCTComponentViewFactory.mm | 6 +- .../Mounting/RCTComponentViewRegistry.h | 18 ++-- .../Mounting/RCTComponentViewRegistry.mm | 97 +++++++++---------- React/Fabric/Mounting/RCTMountingManager.mm | 42 ++++---- React/Fabric/RCTSurfacePresenter.mm | 11 ++- 7 files changed, 112 insertions(+), 93 deletions(-) create mode 100644 React/Fabric/Mounting/RCTComponentViewDescriptor.h diff --git a/React/Fabric/Mounting/RCTComponentViewDescriptor.h b/React/Fabric/Mounting/RCTComponentViewDescriptor.h new file mode 100644 index 00000000000..14c5a39083e --- /dev/null +++ b/React/Fabric/Mounting/RCTComponentViewDescriptor.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +/* + * Holds a native view instance and a set of attributes associated with it. + * Mounting infrastructure uses these objects to bookkeep views and cache their + * attributes for efficient access. + */ +class RCTComponentViewDescriptor final { + public: + /* + * Associated (and owned) native view instance. + */ + UIView *view; +}; + +NS_ASSUME_NONNULL_END diff --git a/React/Fabric/Mounting/RCTComponentViewFactory.h b/React/Fabric/Mounting/RCTComponentViewFactory.h index 959eab96e74..fa4e152c470 100644 --- a/React/Fabric/Mounting/RCTComponentViewFactory.h +++ b/React/Fabric/Mounting/RCTComponentViewFactory.h @@ -7,6 +7,7 @@ #import +#import #import #import @@ -37,8 +38,7 @@ NS_ASSUME_NONNULL_BEGIN /** * Creates a component view with given component handle. */ -- (UIView *)createComponentViewWithComponentHandle: - (facebook::react::ComponentHandle)componentHandle; +- (RCTComponentViewDescriptor)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle; /** * Creates *managed* `ComponentDescriptorRegistry`. After creation, the object continues to store a weak pointer to the diff --git a/React/Fabric/Mounting/RCTComponentViewFactory.mm b/React/Fabric/Mounting/RCTComponentViewFactory.mm index 361842a6f8b..53f2eebdf7a 100644 --- a/React/Fabric/Mounting/RCTComponentViewFactory.mm +++ b/React/Fabric/Mounting/RCTComponentViewFactory.mm @@ -98,8 +98,7 @@ using namespace facebook::react; _providerRegistry.remove(componentDescriptorProvider); } -- (UIView *)createComponentViewWithComponentHandle: - (facebook::react::ComponentHandle)componentHandle +- (RCTComponentViewDescriptor)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle { RCTAssertMainQueue(); std::shared_lock lock(_mutex); @@ -111,7 +110,8 @@ using namespace facebook::react; componentHandle, (char *)componentHandle); Class componentViewClass = iterator->second; - return [[componentViewClass alloc] init]; + + return RCTComponentViewDescriptor{.view = [[componentViewClass alloc] init]}; } - (facebook::react::ComponentDescriptorRegistry::Shared)createComponentDescriptorRegistryWithParameters: diff --git a/React/Fabric/Mounting/RCTComponentViewRegistry.h b/React/Fabric/Mounting/RCTComponentViewRegistry.h index 8b1496e2371..9199d68ac68 100644 --- a/React/Fabric/Mounting/RCTComponentViewRegistry.h +++ b/React/Fabric/Mounting/RCTComponentViewRegistry.h @@ -7,6 +7,7 @@ #import +#import #import #import #import @@ -22,32 +23,31 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, readonly) RCTComponentViewFactory *componentViewFactory; /** - * Returns a native view instance from the recycle pool (or create) + * Returns a descriptor referring to a native view instance from the recycle pool (or being created on demand) * for given `componentHandle` and with given `tag`. * #RefuseSingleUse */ -- (UIView *)dequeueComponentViewWithComponentHandle: - (facebook::react::ComponentHandle)componentHandle - tag:(ReactTag)tag; +- (RCTComponentViewDescriptor)dequeueComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle + tag:(facebook::react::Tag)tag; /** * Puts a given native component view to the recycle pool. * #RefuseSingleUse */ - (void)enqueueComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle - tag:(ReactTag)tag - componentView:(UIView *)componentView; + tag:(facebook::react::Tag)tag + componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor; /** - * Returns a native component view by given `tag`. + * Returns a component view descriptor by given `tag`. */ -- (UIView *)componentViewByTag:(ReactTag)tag; +- (RCTComponentViewDescriptor const &)componentViewDescriptorWithTag:(facebook::react::Tag)tag; /** * Finds a native component view by given `tag`. * Returns `nil` if there is no registered component with the `tag`. */ -- (nullable UIView *)findComponentViewWithTag:(ReactTag)tag; +- (nullable UIView *)findComponentViewWithTag:(facebook::react::Tag)tag; /** * Creates a component view with a given type and puts it to the recycle pool. diff --git a/React/Fabric/Mounting/RCTComponentViewRegistry.mm b/React/Fabric/Mounting/RCTComponentViewRegistry.mm index 074489d12e5..23d08cef831 100644 --- a/React/Fabric/Mounting/RCTComponentViewRegistry.mm +++ b/React/Fabric/Mounting/RCTComponentViewRegistry.mm @@ -14,6 +14,8 @@ #import "RCTParagraphComponentView.h" #import "RCTViewComponentView.h" +#import + using namespace facebook::react; #define LEGACY_UIMANAGER_INTEGRATION_ENABLED 1 @@ -73,18 +75,13 @@ using namespace facebook::react; const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; @implementation RCTComponentViewRegistry { - NSMapTable *> *_registry; - NSMapTable *> *> *_recyclePool; + better::map _registry; + better::map> _recyclePool; } - (instancetype)init { if (self = [super init]) { - _registry = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory - valueOptions:NSPointerFunctionsObjectPersonality]; - _recyclePool = - [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality | NSPointerFunctionsOpaqueMemory - valueOptions:NSPointerFunctionsObjectPersonality]; _componentViewFactory = [RCTComponentViewFactory standardComponentViewFactory]; [[NSNotificationCenter defaultCenter] addObserver:self @@ -123,108 +120,102 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; } } -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -- (UIView *)dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle - tag:(ReactTag)tag +- (RCTComponentViewDescriptor)dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle tag:(Tag)tag { RCTAssertMainQueue(); RCTAssert( - ![_registry objectForKey:(__bridge id)(void *)tag], + _registry.find(tag) == _registry.end(), @"RCTComponentViewRegistry: Attempt to dequeue already registered component."); - UIView *componentView = [self _dequeueComponentViewWithComponentHandle:componentHandle]; - componentView.tag = tag; - [_registry setObject:componentView forKey:(__bridge id)(void *)tag]; + auto componentViewDescriptor = [self _dequeueComponentViewWithComponentHandle:componentHandle]; + componentViewDescriptor.view.tag = tag; + + _registry.insert({tag, componentViewDescriptor}); #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED - [RCTUIManager registerView:componentView]; + [RCTUIManager registerView:componentViewDescriptor.view]; #endif - return componentView; + return componentViewDescriptor; } - (void)enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle - tag:(ReactTag)tag - componentView:(UIView *)componentView + tag:(Tag)tag + componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor { RCTAssertMainQueue(); RCTAssert( - [_registry objectForKey:(__bridge id)(void *)tag], - @"RCTComponentViewRegistry: Attempt to enqueue unregistered component."); + _registry.find(tag) != _registry.end(), @"RCTComponentViewRegistry: Attempt to enqueue unregistered component."); #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED - [RCTUIManager unregisterView:componentView]; + [RCTUIManager unregisterView:componentViewDescriptor.view]; #endif - [_registry removeObjectForKey:(__bridge id)(void *)tag]; - componentView.tag = 0; - [self _enqueueComponentViewWithComponentHandle:componentHandle componentView:componentView]; + _registry.erase(tag); + componentViewDescriptor.view.tag = 0; + [self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor]; } - (void)optimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle { RCTAssertMainQueue(); [self _enqueueComponentViewWithComponentHandle:componentHandle - componentView:[self.componentViewFactory + componentViewDescriptor:[self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]]; } -- (UIView *)componentViewByTag:(ReactTag)tag +- (RCTComponentViewDescriptor const &)componentViewDescriptorWithTag:(Tag)tag { RCTAssertMainQueue(); - UIView *componentView = [_registry objectForKey:(__bridge id)(void *)tag]; - RCTAssert(componentView, @"RCTComponentViewRegistry: Attempt to query unregistered component."); - return componentView; + auto iterator = _registry.find(tag); + RCTAssert(iterator != _registry.end(), @"RCTComponentViewRegistry: Attempt to query unregistered component."); + return iterator->second; } -- (nullable UIView *)findComponentViewWithTag:(ReactTag)tag +- (nullable UIView *)findComponentViewWithTag:(Tag)tag { RCTAssertMainQueue(); - return [_registry objectForKey:(__bridge id)(void *)tag]; + auto iterator = _registry.find(tag); + if (iterator == _registry.end()) { + return nil; + } + return iterator->second.view; } -- (nullable UIView *)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle +- (RCTComponentViewDescriptor)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle { RCTAssertMainQueue(); - NSHashTable *> *componentViews = - [_recyclePool objectForKey:(__bridge id)(void *)componentHandle]; - if (!componentViews || componentViews.count == 0) { + auto &recycledViews = _recyclePool[componentHandle]; + + if (recycledViews.size() == 0) { return [self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]; } - UIView *componentView = [componentViews anyObject]; - [componentViews removeObject:componentView]; - return componentView; + auto componentViewDescriptor = recycledViews.back(); + recycledViews.pop_back(); + return componentViewDescriptor; } - (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle - componentView:(UIView *)componentView + componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor { RCTAssertMainQueue(); - NSHashTable *> *componentViews = - [_recyclePool objectForKey:(__bridge id)(void *)componentHandle]; - if (!componentViews) { - componentViews = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPersonality]; - [_recyclePool setObject:componentViews forKey:(__bridge id)(void *)componentHandle]; - } + auto &recycledViews = _recyclePool[componentHandle]; - if (componentViews.count >= RCTComponentViewRegistryRecyclePoolMaxSize) { + if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) { return; } - [componentView prepareForRecycle]; - [componentViews addObject:componentView]; + [componentViewDescriptor.view prepareForRecycle]; + + recycledViews.push_back(componentViewDescriptor); } - (void)handleApplicationDidReceiveMemoryWarningNotification { - [_recyclePool removeAllObjects]; + _recyclePool.clear(); } @end diff --git a/React/Fabric/Mounting/RCTMountingManager.mm b/React/Fabric/Mounting/RCTMountingManager.mm index 385409dfc72..362513ebfda 100644 --- a/React/Fabric/Mounting/RCTMountingManager.mm +++ b/React/Fabric/Mounting/RCTMountingManager.mm @@ -34,11 +34,10 @@ static void RNCreateMountInstruction(ShadowViewMutation const &mutation, RCTComp static void RNDeleteMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &oldChildShadowView = mutation.oldChildShadowView; - UIView *componentView = [registry componentViewByTag:oldChildShadowView.tag]; - + RCTComponentViewDescriptor componentViewDescriptor = [registry componentViewDescriptorWithTag:oldChildShadowView.tag]; [registry enqueueComponentViewWithComponentHandle:oldChildShadowView.componentHandle tag:oldChildShadowView.tag - componentView:componentView]; + componentViewDescriptor:componentViewDescriptor]; } // `Insert` instruction @@ -47,10 +46,10 @@ static void RNInsertMountInstruction(ShadowViewMutation const &mutation, RCTComp auto const &newShadowView = mutation.newChildShadowView; auto const &parentShadowView = mutation.parentShadowView; - UIView *childComponentView = [registry componentViewByTag:newShadowView.tag]; - UIView *parentComponentView = [registry componentViewByTag:parentShadowView.tag]; + auto const &childComponentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + auto const &parentComponentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag]; - [parentComponentView mountChildComponentView:childComponentView index:mutation.index]; + [parentComponentViewDescriptor.view mountChildComponentView:childComponentViewDescriptor.view index:mutation.index]; } // `Remove` instruction @@ -59,10 +58,10 @@ static void RNRemoveMountInstruction(ShadowViewMutation const &mutation, RCTComp auto const &oldShadowView = mutation.oldChildShadowView; auto const &parentShadowView = mutation.parentShadowView; - UIView *childComponentView = [registry componentViewByTag:oldShadowView.tag]; - UIView *parentComponentView = [registry componentViewByTag:parentShadowView.tag]; + auto const &childComponentViewDescriptor = [registry componentViewDescriptorWithTag:oldShadowView.tag]; + auto const &parentComponentViewDescriptor = [registry componentViewDescriptorWithTag:parentShadowView.tag]; - [parentComponentView unmountChildComponentView:childComponentView index:mutation.index]; + [parentComponentViewDescriptor.view unmountChildComponentView:childComponentViewDescriptor.view index:mutation.index]; } // `Update Props` instruction @@ -70,16 +69,16 @@ static void RNUpdatePropsMountInstruction(ShadowViewMutation const &mutation, RC { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView updateProps:newShadowView.props oldProps:oldShadowView.props]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view updateProps:newShadowView.props oldProps:oldShadowView.props]; } // `Update EventEmitter` instruction static void RNUpdateEventEmitterMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry) { auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView updateEventEmitter:newShadowView.eventEmitter]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view updateEventEmitter:newShadowView.eventEmitter]; } // `Update LayoutMetrics` instruction @@ -89,8 +88,9 @@ static void RNUpdateLayoutMetricsMountInstruction( { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView updateLayoutMetrics:newShadowView.layoutMetrics oldLayoutMetrics:oldShadowView.layoutMetrics]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view updateLayoutMetrics:newShadowView.layoutMetrics + oldLayoutMetrics:oldShadowView.layoutMetrics]; } // `Update LocalData` instruction @@ -98,8 +98,8 @@ static void RNUpdateLocalDataMountInstruction(ShadowViewMutation const &mutation { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView updateLocalData:newShadowView.localData oldLocalData:oldShadowView.localData]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view updateLocalData:newShadowView.localData oldLocalData:oldShadowView.localData]; } // `Update State` instruction @@ -107,8 +107,8 @@ static void RNUpdateStateMountInstruction(ShadowViewMutation const &mutation, RC { auto const &oldShadowView = mutation.oldChildShadowView; auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView updateState:newShadowView.state oldState:oldShadowView.state]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view updateState:newShadowView.state oldState:oldShadowView.state]; } // `Finalize Updates` instruction @@ -118,8 +118,8 @@ static void RNFinalizeUpdatesMountInstruction( RCTComponentViewRegistry *registry) { auto const &newShadowView = mutation.newChildShadowView; - UIView *componentView = [registry componentViewByTag:newShadowView.tag]; - [componentView finalizeUpdates:mask]; + auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag]; + [componentViewDescriptor.view finalizeUpdates:mask]; } // `Update` instruction diff --git a/React/Fabric/RCTSurfacePresenter.mm b/React/Fabric/RCTSurfacePresenter.mm index 973fa9fcef2..95a703af466 100644 --- a/React/Fabric/RCTSurfacePresenter.mm +++ b/React/Fabric/RCTSurfacePresenter.mm @@ -259,11 +259,11 @@ using namespace facebook::react; RCTMountingManager *mountingManager = _mountingManager; RCTExecuteOnMainQueue(^{ - UIView *rootView = - [mountingManager.componentViewRegistry componentViewByTag:surface.rootTag]; + RCTComponentViewDescriptor rootViewDescriptor = + [mountingManager.componentViewRegistry componentViewDescriptorWithTag:surface.rootTag]; [mountingManager.componentViewRegistry enqueueComponentViewWithComponentHandle:RootShadowNode::Handle() tag:surface.rootTag - componentView:rootView]; + componentViewDescriptor:rootViewDescriptor]; }); [surface _unsetStage:(RCTSurfaceStagePrepared | RCTSurfaceStageMounted)]; @@ -344,8 +344,9 @@ using namespace facebook::react; if (stage & RCTSurfaceStagePrepared) { // We have to progress the stage only if the preparing phase is done. if ([surface _setStage:RCTSurfaceStageMounted]) { - UIView *rootComponentView = [_mountingManager.componentViewRegistry componentViewByTag:rootTag]; - surface.view.rootView = (RCTSurfaceRootView *)rootComponentView; + auto rootComponentViewDescriptor = + [_mountingManager.componentViewRegistry componentViewDescriptorWithTag:rootTag]; + surface.view.rootView = (RCTSurfaceRootView *)rootComponentViewDescriptor.view; } }