Introducing RCTComponentViewFactory

Summary:
The whole mounting iOS infra now uses `ComponentHandle` instead of `std::string` as a reference to particular `ComponentView` implementation. All changes are pretty straightforward, we use a different thing/type to refer to the particular class; no changes in the logic besides a new `RCTComponentViewFactory` that serves the same role of classes registry as Objective-C runtime served previously.
That has several benefits:
* It should be slightly faster, mostly because we don't need to convert `char *` strings to `std::string` and then to `NSString *`.
* We don't need string-based component-name maps anymore (at least on this layer). We can call classes as we want and it will work because of classes are now explicit about which ShadowNodes they are compatible with.
* Most importantly, it's explicit now! That means that no runtime magic is involved anymore and we can rely on static linting tool now and not be afraid of improper code stripping/overoptimization.

Reviewed By: mdvacca

Differential Revision: D13130760

fbshipit-source-id: aadf70525a1335b96992443abae4da359efdc829
This commit is contained in:
Valentin Shergin 2018-11-25 22:15:00 -08:00 committed by Facebook Github Bot
parent eef3df86fb
commit e581977b51
14 changed files with 186 additions and 66 deletions

View File

@ -7,6 +7,7 @@
#import <UIKit/UIKit.h>
#import <react/core/ReactPrimitives.h>
#import <React/RCTMountItemProtocol.h>
#import <React/RCTPrimitives.h>
@ -19,8 +20,8 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface RCTCreateMountItem : NSObject <RCTMountItemProtocol>
- (instancetype)initWithComponentName:(NSString *)componentName
tag:(ReactTag)tag;
- (instancetype)initWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag;
@end

View File

@ -9,16 +9,18 @@
#import "RCTComponentViewRegistry.h"
using namespace facebook::react;
@implementation RCTCreateMountItem {
NSString *_componentName;
ComponentHandle _componentHandle;
ReactTag _tag;
}
- (instancetype)initWithComponentName:(NSString *)componentName
tag:(ReactTag)tag
- (instancetype)initWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag
{
if (self = [super init]) {
_componentName = componentName;
_componentHandle = componentHandle;
_tag = tag;
}
@ -27,7 +29,7 @@
- (void)executeWithRegistry:(RCTComponentViewRegistry *)registry
{
[registry dequeueComponentViewWithName:_componentName tag:_tag];
[registry dequeueComponentViewWithComponentHandle:_componentHandle tag:_tag];
}
@end

View File

@ -7,6 +7,7 @@
#import <UIKit/UIKit.h>
#import <react/core/ReactPrimitives.h>
#import <React/RCTMountItemProtocol.h>
#import <React/RCTPrimitives.h>
@ -17,8 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface RCTDeleteMountItem : NSObject <RCTMountItemProtocol>
- (instancetype)initWithComponentName:(NSString *)componentName
tag:(ReactTag)tag;
- (instancetype)initWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag;
@end

View File

@ -9,16 +9,18 @@
#import "RCTComponentViewRegistry.h"
using namespace facebook::react;
@implementation RCTDeleteMountItem {
NSString *_componentName;
ComponentHandle _componentHandle;
ReactTag _tag;
}
- (instancetype)initWithComponentName:(NSString *)componentName
tag:(ReactTag)tag
- (instancetype)initWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag
{
if (self = [super init]) {
_componentName = componentName;
_componentHandle = componentHandle;
_tag = tag;
}
@ -33,7 +35,7 @@
return;
}
[registry enqueueComponentViewWithName:_componentName tag:_tag componentView:componentView];
[registry enqueueComponentViewWithComponentHandle:_componentHandle tag:_tag componentView:componentView];
}
@end

View File

@ -0,0 +1,37 @@
/**
* 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
/**
* Registry of supported component view classes that can instantiate
* view component instances by given component handle.
*/
@interface RCTComponentViewFactory : NSObject
/**
* Constructs and returns an instance of the class with a bunch of already registered standard components.
*/
+ (RCTComponentViewFactory *)standardComponentViewFactory;
/**
* Registers a component view class in the factory.
*/
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass;
/**
* Creates a component view with given component handle.
*/
- (UIView<RCTComponentViewProtocol> *)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,65 @@
/**
* 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 "RCTComponentViewFactory.h"
#import <React/RCTAssert.h>
#import <react/core/ReactPrimitives.h>
#import "RCTViewComponentView.h"
#import "RCTImageComponentView.h"
#import "RCTScrollViewComponentView.h"
#import "RCTParagraphComponentView.h"
#import "RCTRootComponentView.h"
#import "RCTActivityIndicatorViewComponentView.h"
#import "RCTSwitchComponentView.h"
using namespace facebook::react;
@implementation RCTComponentViewFactory
{
std::unordered_map<ComponentHandle, Class<RCTComponentViewProtocol>> _registry;
}
+ (RCTComponentViewFactory *)standardComponentViewFactory
{
RCTAssertMainQueue();
RCTComponentViewFactory *componentViewFactory = [[RCTComponentViewFactory alloc] init];
[componentViewFactory registerComponentViewClass:[RCTViewComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTRootComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTScrollViewComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTImageComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTParagraphComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTActivityIndicatorViewComponentView class]];
[componentViewFactory registerComponentViewClass:[RCTSwitchComponentView class]];
return componentViewFactory;
}
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass
{
RCTAssertMainQueue();
ComponentHandle componentHandle = [componentViewClass componentHandle];
_registry[componentHandle] = componentViewClass;
}
- (UIView<RCTComponentViewProtocol> *)createComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
{
RCTAssertMainQueue();
auto iterator = _registry.find(componentHandle);
RCTAssert(
iterator != _registry.end(),
@"ComponentView with componentHandle `%lli` (`%s`) not found.", componentHandle, (char *)componentHandle);
Class componentViewClass = iterator->second;
return [[componentViewClass alloc] init];
}
@end

View File

@ -7,6 +7,8 @@
#import <UIKit/UIKit.h>
#import <react/core/ReactPrimitives.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTComponentViewProtocol.h>
NS_ASSUME_NONNULL_BEGIN
@ -17,21 +19,23 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface RCTComponentViewRegistry : NSObject
@property (nonatomic, strong, readonly) RCTComponentViewFactory *componentViewFactory;
/**
* Returns a native view instance from the recycle pool (or create)
* for given `componentName` and with given `tag`.
* for given `componentHandle` and with given `tag`.
* #RefuseSingleUse
*/
- (UIView<RCTComponentViewProtocol> *)dequeueComponentViewWithName:(NSString *)componentName
tag:(ReactTag)tag;
- (UIView<RCTComponentViewProtocol> *)dequeueComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag;
/**
* Puts a given native component view to the recycle pool.
* #RefuseSingleUse
*/
- (void)enqueueComponentViewWithName:(NSString *)componentName
tag:(ReactTag)tag
componentView:(UIView<RCTComponentViewProtocol> *)componentView;
- (void)enqueueComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle
tag:(ReactTag)tag
componentView:(UIView<RCTComponentViewProtocol> *)componentView;
/**
* Returns a native component view by given `tag`.
@ -46,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Creates a component view with a given type and puts it to the recycle pool.
*/
- (void)preliminaryCreateComponentViewWithName:(NSString *)componentName;
- (void)optimisticallyCreateComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle;
@end

View File

@ -10,6 +10,8 @@
#import <Foundation/NSMapTable.h>
#import <React/RCTAssert.h>
using namespace facebook::react;
#define LEGACY_UIMANAGER_INTEGRATION_ENABLED 1
#ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
@ -67,8 +69,8 @@
const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
@implementation RCTComponentViewRegistry {
NSMapTable<id, UIView<RCTComponentViewProtocol> *> *_registry;
NSMapTable<NSString *, NSHashTable<UIView<RCTComponentViewProtocol> *> *> *_recyclePool;
NSMapTable<id /* ReactTag */, UIView<RCTComponentViewProtocol> *> *_registry;
NSMapTable<id /* ComponentHandle */, NSHashTable<UIView<RCTComponentViewProtocol> *> *> *_recyclePool;
}
- (instancetype)init
@ -76,15 +78,16 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
if (self = [super init]) {
_registry = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsIntegerPersonality | NSPointerFunctionsOpaqueMemory
valueOptions:NSPointerFunctionsObjectPersonality];
_recyclePool = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsObjectPersonality
_recyclePool = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaquePersonality | NSPointerFunctionsOpaqueMemory
valueOptions:NSPointerFunctionsObjectPersonality];
_componentViewFactory = [RCTComponentViewFactory standardComponentViewFactory];
}
return self;
}
- (UIView<RCTComponentViewProtocol> *)dequeueComponentViewWithName:(NSString *)componentName
tag:(ReactTag)tag
- (UIView<RCTComponentViewProtocol> *)dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
tag:(ReactTag)tag
{
RCTAssertMainQueue();
@ -92,7 +95,7 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
@"RCTComponentViewRegistry: Attempt to dequeue already registered component.");
UIView<RCTComponentViewProtocol> *componentView =
[self _dequeueComponentViewWithName:componentName];
[self _dequeueComponentViewWithComponentHandle:componentHandle];
componentView.tag = tag;
[_registry setObject:componentView forKey:(__bridge id)(void *)tag];
@ -103,9 +106,9 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
return componentView;
}
- (void)enqueueComponentViewWithName:(NSString *)componentName
tag:(ReactTag)tag
componentView:(UIView<RCTComponentViewProtocol> *)componentView
- (void)enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
tag:(ReactTag)tag
componentView:(UIView<RCTComponentViewProtocol> *)componentView
{
RCTAssertMainQueue();
@ -118,14 +121,14 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
[_registry removeObjectForKey:(__bridge id)(void *)tag];
componentView.tag = 0;
[self _enqueueComponentViewWithName:componentName componentView:componentView];
[self _enqueueComponentViewWithComponentHandle:componentHandle componentView:componentView];
}
- (void)preliminaryCreateComponentViewWithName:(NSString *)componentName
- (void)optimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle
{
RCTAssertMainQueue();
[self _enqueueComponentViewWithName:componentName
componentView:[self _createComponentViewWithName:componentName]];
[self _enqueueComponentViewWithComponentHandle:componentHandle
componentView:[self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]];
}
- (UIView<RCTComponentViewProtocol> *)componentViewByTag:(ReactTag)tag
@ -140,21 +143,13 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
return componentView.tag;
}
- (UIView<RCTComponentViewProtocol> *)_createComponentViewWithName:(NSString *)componentName
- (nullable UIView<RCTComponentViewProtocol> *)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
{
RCTAssertMainQueue();
// This is temporary approach.
NSString *className = [NSString stringWithFormat:@"RCT%@ComponentView", componentName];
UIView<RCTComponentViewProtocol> *componentView = [[NSClassFromString(className) alloc] init];
return componentView;
}
- (nullable UIView<RCTComponentViewProtocol> *)_dequeueComponentViewWithName:(NSString *)componentName
{
RCTAssertMainQueue();
NSHashTable<UIView<RCTComponentViewProtocol> *> *componentViews = [_recyclePool objectForKey:componentName];
NSHashTable<UIView<RCTComponentViewProtocol> *> *componentViews =
[_recyclePool objectForKey:(__bridge id)(void *)componentHandle];
if (!componentViews || componentViews.count == 0) {
return [self _createComponentViewWithName:componentName];
return [self.componentViewFactory createComponentViewWithComponentHandle:componentHandle];
}
UIView<RCTComponentViewProtocol> *componentView = [componentViews anyObject];
@ -162,16 +157,17 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
return componentView;
}
- (void)_enqueueComponentViewWithName:(NSString *)componentName
componentView:(UIView<RCTComponentViewProtocol> *)componentView
- (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
componentView:(UIView<RCTComponentViewProtocol> *)componentView
{
RCTAssertMainQueue();
[componentView prepareForRecycle];
NSHashTable<UIView<RCTComponentViewProtocol> *> *componentViews = [_recyclePool objectForKey:componentName];
NSHashTable<UIView<RCTComponentViewProtocol> *> *componentViews =
[_recyclePool objectForKey:(__bridge id)(void *)componentHandle];
if (!componentViews) {
componentViews = [NSHashTable hashTableWithOptions:NSPointerFunctionsObjectPersonality];
[_recyclePool setObject:componentViews forKey:componentName];
[_recyclePool setObject:componentViews forKey:(__bridge id)(void *)componentHandle];
}
if (componentViews.count >= RCTComponentViewRegistryRecyclePoolMaxSize) {

View File

@ -7,6 +7,7 @@
#import <UIKit/UIKit.h>
#import <react/core/ReactPrimitives.h>
#import <react/uimanager/ShadowView.h>
#import <react/uimanager/ShadowViewMutation.h>
#import <React/RCTPrimitives.h>
@ -37,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
* The receiver is free to ignore the request.
* Can be called from any thread.
*/
- (void)preliminaryCreateComponentViewWithName:(NSString *)componentName;
- (void)optimisticallyCreateComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle;
@end

View File

@ -47,19 +47,17 @@ using namespace facebook::react;
for (const auto &mutation : mutations) {
switch (mutation.type) {
case ShadowViewMutation::Create: {
NSString *componentName = RCTNSStringFromString(mutation.newChildShadowView.componentName, NSASCIIStringEncoding);
RCTCreateMountItem *mountItem =
[[RCTCreateMountItem alloc] initWithComponentName:componentName
tag:mutation.newChildShadowView.tag];
[[RCTCreateMountItem alloc] initWithComponentHandle:mutation.newChildShadowView.componentHandle
tag:mutation.newChildShadowView.tag];
[mountItems addObject:mountItem];
break;
}
case ShadowViewMutation::Delete: {
NSString *componentName = RCTNSStringFromString(mutation.oldChildShadowView.componentName, NSASCIIStringEncoding);
RCTDeleteMountItem *mountItem =
[[RCTDeleteMountItem alloc] initWithComponentName:componentName
tag:mutation.oldChildShadowView.tag];
[[RCTDeleteMountItem alloc] initWithComponentHandle:mutation.oldChildShadowView.componentHandle
tag:mutation.oldChildShadowView.tag];
[mountItems addObject:mountItem];
break;
}
@ -170,10 +168,10 @@ using namespace facebook::react;
[self.delegate mountingManager:self didMountComponentsWithRootTag:rootTag];
}
- (void)preliminaryCreateComponentViewWithName:(NSString *)componentName
- (void)optimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle
{
RCTExecuteOnMainQueue(^{
[self->_componentViewRegistry preliminaryCreateComponentViewWithName:componentName];
[self->_componentViewRegistry optimisticallyCreateComponentViewWithComponentHandle:componentHandle];
});
}

View File

@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)schedulerDidFinishTransaction:(facebook::react::ShadowViewMutationList)mutations
rootTag:(ReactTag)rootTag;
- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName;
- (void)schedulerOptimisticallyCreateComponentViewWithComponentHandle:(facebook::react::ComponentHandle)componentHandle;
@end

View File

@ -29,7 +29,7 @@ public:
void schedulerDidRequestPreliminaryViewAllocation(SurfaceId surfaceId, ComponentName componentName, bool isLayoutable, ComponentHandle componentHandle) override {
RCTScheduler *scheduler = (__bridge RCTScheduler *)scheduler_;
[scheduler.delegate schedulerDidRequestPreliminaryViewAllocationWithComponentName:RCTNSStringFromString(componentName, NSASCIIStringEncoding)];
[scheduler.delegate schedulerOptimisticallyCreateComponentViewWithComponentHandle:componentHandle];
}
private:

View File

@ -9,6 +9,7 @@
#import <memory>
#import <React/RCTBridge.h>
#import <React/RCTComponentViewFactory.h>
#import <React/RCTPrimitives.h>
NS_ASSUME_NONNULL_BEGIN
@ -26,6 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithBridge:(RCTBridge *)bridge;
@property (nonatomic, readonly) RCTComponentViewFactory *componentViewFactory;
@end
@interface RCTSurfacePresenter (Surface)

View File

@ -26,6 +26,7 @@
#import <React/RCTUtils.h>
#import <react/core/LayoutContext.h>
#import <react/core/LayoutConstraints.h>
#import <react/components/root/RootShadowNode.h>
#import <react/imagemanager/ImageManager.h>
#import <react/uimanager/ContextContainer.h>
@ -80,6 +81,11 @@ using namespace facebook::react;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (RCTComponentViewFactory *)componentViewFactory
{
return _mountingManager.componentViewRegistry.componentViewFactory;
}
#pragma mark - Internal Surface-dedicated Interface
- (void)registerSurface:(RCTFabricSurface *)surface
@ -188,7 +194,8 @@ using namespace facebook::react;
- (void)_startSurface:(RCTFabricSurface *)surface
{
[_mountingManager.componentViewRegistry dequeueComponentViewWithName:@"Root" tag:surface.rootTag];
[_mountingManager.componentViewRegistry dequeueComponentViewWithComponentHandle:RootShadowNode::Handle()
tag:surface.rootTag];
LayoutContext layoutContext = {
.pointScaleFactor = RCTScreenScale()
@ -210,8 +217,11 @@ using namespace facebook::react;
{
[self._scheduler stopSurfaceWithSurfaceId:surface.rootTag];
UIView<RCTComponentViewProtocol> *rootView = [_mountingManager.componentViewRegistry componentViewByTag:surface.rootTag];
[_mountingManager.componentViewRegistry enqueueComponentViewWithName:@"Root" tag:surface.rootTag componentView:rootView];
UIView<RCTComponentViewProtocol> *rootView =
[_mountingManager.componentViewRegistry componentViewByTag:surface.rootTag];
[_mountingManager.componentViewRegistry enqueueComponentViewWithComponentHandle:RootShadowNode::Handle()
tag:surface.rootTag
componentView:rootView];
[surface _unsetStage:(RCTSurfaceStagePrepared | RCTSurfaceStageMounted)];
}
@ -243,9 +253,9 @@ using namespace facebook::react;
rootTag:rootTag];
}
- (void)schedulerDidRequestPreliminaryViewAllocationWithComponentName:(NSString *)componentName
- (void)schedulerOptimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle
{
[_mountingManager preliminaryCreateComponentViewWithName:componentName];
[_mountingManager optimisticallyCreateComponentViewWithComponentHandle:componentHandle];
}
#pragma mark - RCTMountingManagerDelegate