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
This commit is contained in:
Valentin Shergin
2019-11-01 09:38:24 -07:00
committed by Facebook Github Bot
parent 8a1937ace6
commit 93ded152d9
7 changed files with 112 additions and 93 deletions
@@ -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 <UIKit/UIKit.h>
#import <React/RCTComponentViewProtocol.h>
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<RCTComponentViewProtocol> *view;
};
NS_ASSUME_NONNULL_END
@@ -7,6 +7,7 @@
#import <UIKit/UIKit.h>
#import <React/RCTComponentViewDescriptor.h>
#import <React/RCTComponentViewProtocol.h>
#import <react/uimanager/ComponentDescriptorRegistry.h>
@@ -37,8 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Creates a component view with given component handle.
*/
- (UIView<RCTComponentViewProtocol> *)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
@@ -98,8 +98,7 @@ using namespace facebook::react;
_providerRegistry.remove(componentDescriptorProvider);
}
- (UIView<RCTComponentViewProtocol> *)createComponentViewWithComponentHandle:
(facebook::react::ComponentHandle)componentHandle
- (RCTComponentViewDescriptor)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
{
RCTAssertMainQueue();
std::shared_lock<better::shared_mutex> 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:
@@ -7,6 +7,7 @@
#import <UIKit/UIKit.h>
#import <React/RCTComponentViewDescriptor.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
#import <react/core/ReactPrimitives.h>
@@ -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<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *)findComponentViewWithTag:(ReactTag)tag;
- (nullable UIView<RCTComponentViewProtocol> *)findComponentViewWithTag:(facebook::react::Tag)tag;
/**
* Creates a component view with a given type and puts it to the recycle pool.
@@ -14,6 +14,8 @@
#import "RCTParagraphComponentView.h"
#import "RCTViewComponentView.h"
#import <better/map.h>
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<id /* ReactTag */, UIView<RCTComponentViewProtocol> *> *_registry;
NSMapTable<id /* ComponentHandle */, NSHashTable<UIView<RCTComponentViewProtocol> *> *> *_recyclePool;
better::map<Tag, RCTComponentViewDescriptor> _registry;
better::map<ComponentHandle, std::vector<RCTComponentViewDescriptor>> _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<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *)componentViewByTag:(ReactTag)tag
- (RCTComponentViewDescriptor const &)componentViewDescriptorWithTag:(Tag)tag
{
RCTAssertMainQueue();
UIView<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *)findComponentViewWithTag:(ReactTag)tag
- (nullable UIView<RCTComponentViewProtocol> *)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<RCTComponentViewProtocol> *)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
- (RCTComponentViewDescriptor)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
{
RCTAssertMainQueue();
NSHashTable<UIView<RCTComponentViewProtocol> *> *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<RCTComponentViewProtocol> *componentView = [componentViews anyObject];
[componentViews removeObject:componentView];
return componentView;
auto componentViewDescriptor = recycledViews.back();
recycledViews.pop_back();
return componentViewDescriptor;
}
- (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
componentView:(UIView<RCTComponentViewProtocol> *)componentView
componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor
{
RCTAssertMainQueue();
NSHashTable<UIView<RCTComponentViewProtocol> *> *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
+21 -21
View File
@@ -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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *childComponentView = [registry componentViewByTag:newShadowView.tag];
UIView<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *childComponentView = [registry componentViewByTag:oldShadowView.tag];
UIView<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *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<RCTComponentViewProtocol> *componentView = [registry componentViewByTag:newShadowView.tag];
[componentView finalizeUpdates:mask];
auto const &componentViewDescriptor = [registry componentViewDescriptorWithTag:newShadowView.tag];
[componentViewDescriptor.view finalizeUpdates:mask];
}
// `Update` instruction
+6 -5
View File
@@ -259,11 +259,11 @@ using namespace facebook::react;
RCTMountingManager *mountingManager = _mountingManager;
RCTExecuteOnMainQueue(^{
UIView<RCTComponentViewProtocol> *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;
}
}