Fabric: Implementation of RCTMountingTransactionObserving protocol

Summary:
This diff finally uses all facilities from the previous diffs to build an implementation of  `RCTMountingTransactionObserving` protocol which does *not* require using expensive Objective-C runtime features.

In the coming diffs, we will see how it can/should be used.

Changelog: [Internal] Fabric-specific internal change.

Reviewed By: sammy-SC

Differential Revision: D18217101

fbshipit-source-id: 34f411dcb527dc81570c2f2833ce13b40e1450db
This commit is contained in:
Valentin Shergin
2019-11-01 09:38:25 -07:00
committed by Facebook Github Bot
parent 20a0f224ba
commit 41cf6da074
8 changed files with 211 additions and 12 deletions
@@ -20,6 +20,13 @@ class RCTComponentViewClassDescriptor final {
* Associated (and owned) native view class.
*/
Class<RCTComponentViewProtocol> viewClass;
/*
* Indicates a requirement to call on the view methods from
* `RCTMountingTransactionObserving` protocol.
*/
bool observesMountingTransactionWillMount{false};
bool observesMountingTransactionDidMount{false};
};
NS_ASSUME_NONNULL_END
@@ -22,6 +22,31 @@ class RCTComponentViewDescriptor final {
* Associated (and owned) native view instance.
*/
UIView<RCTComponentViewProtocol> *view;
/*
* Indicates a requirement to call on the view methods from
* `RCTMountingTransactionObserving` protocol.
*/
bool observesMountingTransactionWillMount{false};
bool observesMountingTransactionDidMount{false};
};
inline bool operator==(RCTComponentViewDescriptor const &lhs, RCTComponentViewDescriptor const &rhs)
{
return lhs.view == rhs.view;
}
inline bool operator!=(RCTComponentViewDescriptor const &lhs, RCTComponentViewDescriptor const &rhs)
{
return lhs.view != rhs.view;
}
template <>
struct std::hash<RCTComponentViewDescriptor> {
size_t operator()(RCTComponentViewDescriptor const &componentViewDescriptor) const
{
return std::hash<void *>()((__bridge void *)componentViewDescriptor.view);
}
};
NS_ASSUME_NONNULL_END
@@ -9,6 +9,7 @@
#import <React/RCTAssert.h>
#import <React/RCTConversions.h>
#import <better/map.h>
#import <better/mutex.h>
@@ -21,6 +22,7 @@
#import "RCTImageComponentView.h"
#import "RCTLegacyViewManagerInteropComponentView.h"
#import "RCTModalHostViewComponentView.h"
#import "RCTMountingTransactionObserving.h"
#import "RCTParagraphComponentView.h"
#import "RCTPullToRefreshViewComponentView.h"
#import "RCTRootComponentView.h"
@@ -31,6 +33,8 @@
#import "RCTUnimplementedNativeComponentView.h"
#import "RCTViewComponentView.h"
#import <objc/runtime.h>
using namespace facebook::react;
@implementation RCTComponentViewFactory {
@@ -79,7 +83,17 @@ using namespace facebook::react;
- (RCTComponentViewClassDescriptor)_componentViewClassDescriptorFromClass:(Class<RCTComponentViewProtocol>)viewClass
{
return RCTComponentViewClassDescriptor{.viewClass = viewClass};
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
return RCTComponentViewClassDescriptor
{
.viewClass = viewClass,
.observesMountingTransactionWillMount =
(bool)class_respondsToSelector(viewClass, @selector(mountingTransactionWillMountWithMetadata:)),
.observesMountingTransactionDidMount =
(bool)class_respondsToSelector(viewClass, @selector(mountingTransactionDidMountWithMetadata:)),
};
#pragma clang diagnostic pop
}
- (void)registerComponentViewClass:(Class<RCTComponentViewProtocol>)componentViewClass
@@ -120,7 +134,11 @@ using namespace facebook::react;
auto componentViewClassDescriptor = iterator->second;
Class viewClass = componentViewClassDescriptor.viewClass;
return RCTComponentViewDescriptor{.view = [[viewClass alloc] init]};
return RCTComponentViewDescriptor{
.view = [[viewClass alloc] init],
.observesMountingTransactionWillMount = componentViewClassDescriptor.observesMountingTransactionWillMount,
.observesMountingTransactionDidMount = componentViewClassDescriptor.observesMountingTransactionDidMount,
};
}
- (facebook::react::ComponentDescriptorRegistry::Shared)createComponentDescriptorRegistryWithParameters:
@@ -77,6 +77,8 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
@implementation RCTComponentViewRegistry {
better::map<Tag, RCTComponentViewDescriptor> _registry;
better::map<ComponentHandle, std::vector<RCTComponentViewDescriptor>> _recyclePool;
std::unordered_set<RCTComponentViewDescriptor> _mountingTransactionObservers;
}
- (instancetype)init
@@ -133,6 +135,11 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
_registry.insert({tag, componentViewDescriptor});
if (componentViewDescriptor.observesMountingTransactionDidMount ||
componentViewDescriptor.observesMountingTransactionWillMount) {
_mountingTransactionObservers.insert(componentViewDescriptor);
}
#ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
[RCTUIManager registerView:componentViewDescriptor.view];
#endif
@@ -153,6 +160,11 @@ const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
[RCTUIManager unregisterView:componentViewDescriptor.view];
#endif
if (componentViewDescriptor.observesMountingTransactionDidMount ||
componentViewDescriptor.observesMountingTransactionWillMount) {
_mountingTransactionObservers.erase(componentViewDescriptor);
}
_registry.erase(tag);
componentViewDescriptor.view.tag = 0;
[self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor];
+32 -9
View File
@@ -19,22 +19,35 @@
#import "RCTComponentViewProtocol.h"
#import "RCTComponentViewRegistry.h"
#import "RCTConversions.h"
#import "RCTMountingTransactionObserverCoordinator.h"
using namespace facebook;
using namespace facebook::react;
// `Create` instruction
static void RNCreateMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry)
static void RNCreateMountInstruction(
ShadowViewMutation const &mutation,
RCTComponentViewRegistry *registry,
RCTMountingTransactionObserverCoordinator &observerCoordinator,
SurfaceId surfaceId)
{
[registry dequeueComponentViewWithComponentHandle:mutation.newChildShadowView.componentHandle
tag:mutation.newChildShadowView.tag];
auto componentViewDescriptor =
[registry dequeueComponentViewWithComponentHandle:mutation.newChildShadowView.componentHandle
tag:mutation.newChildShadowView.tag];
observerCoordinator.registerViewComponentDescriptor(componentViewDescriptor, surfaceId);
}
// `Delete` instruction
static void RNDeleteMountInstruction(ShadowViewMutation const &mutation, RCTComponentViewRegistry *registry)
static void RNDeleteMountInstruction(
ShadowViewMutation const &mutation,
RCTComponentViewRegistry *registry,
RCTMountingTransactionObserverCoordinator &observerCoordinator,
SurfaceId surfaceId)
{
auto const &oldChildShadowView = mutation.oldChildShadowView;
RCTComponentViewDescriptor componentViewDescriptor = [registry componentViewDescriptorWithTag:oldChildShadowView.tag];
observerCoordinator.unregisterViewComponentDescriptor(componentViewDescriptor, surfaceId);
[registry enqueueComponentViewWithComponentHandle:oldChildShadowView.componentHandle
tag:oldChildShadowView.tag
componentViewDescriptor:componentViewDescriptor];
@@ -123,18 +136,22 @@ static void RNFinalizeUpdatesMountInstruction(
}
// `Update` instruction
static void RNPerformMountInstructions(ShadowViewMutationList const &mutations, RCTComponentViewRegistry *registry)
static void RNPerformMountInstructions(
ShadowViewMutationList const &mutations,
RCTComponentViewRegistry *registry,
RCTMountingTransactionObserverCoordinator &observerCoordinator,
SurfaceId surfaceId)
{
SystraceSection s("RNPerformMountInstructions");
for (auto const &mutation : mutations) {
switch (mutation.type) {
case ShadowViewMutation::Create: {
RNCreateMountInstruction(mutation, registry);
RNCreateMountInstruction(mutation, registry, observerCoordinator, surfaceId);
break;
}
case ShadowViewMutation::Delete: {
RNDeleteMountInstruction(mutation, registry);
RNDeleteMountInstruction(mutation, registry, observerCoordinator, surfaceId);
break;
}
case ShadowViewMutation::Insert: {
@@ -188,7 +205,9 @@ static void RNPerformMountInstructions(ShadowViewMutationList const &mutations,
}
}
@implementation RCTMountingManager
@implementation RCTMountingManager {
RCTMountingTransactionObserverCoordinator _observerCoordinator;
}
- (instancetype)init
{
@@ -250,8 +269,12 @@ static void RNPerformMountInstructions(ShadowViewMutationList const &mutations,
}
RCTAssertMainQueue();
auto metadata = MountingTransactionMetadata{surfaceId, transaction->getNumber(), transaction->getTelemetry()};
[self.delegate mountingManager:self willMountComponentsWithRootTag:surfaceId];
RNPerformMountInstructions(mutations, self.componentViewRegistry);
_observerCoordinator.notifyObserversMountingTransactionWillMount(metadata);
RNPerformMountInstructions(mutations, self.componentViewRegistry, _observerCoordinator, surfaceId);
_observerCoordinator.notifyObserversMountingTransactionDidMount(metadata);
[self.delegate mountingManager:self didMountComponentsWithRootTag:surfaceId];
}
@@ -0,0 +1,42 @@
/*
* 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 <React/RCTComponentViewDescriptor.h>
#import "RCTMountingTransactionObserverCoordinator.h"
#import <better/map.h>
#import <better/set.h>
#import <react/mounting/MountingTransactionMetadata.h>
class RCTMountingTransactionObserverCoordinator final {
public:
/*
* Registers (and unregisters) specified `componentViewDescriptor` in the registry of views that need to be notified.
* Does nothing if a particular `componentViewDescriptor` does not listen the events.
*/
void registerViewComponentDescriptor(
RCTComponentViewDescriptor const &componentViewDescriptor,
facebook::react::SurfaceId surfaceId);
void unregisterViewComponentDescriptor(
RCTComponentViewDescriptor const &componentViewDescriptor,
facebook::react::SurfaceId surfaceId);
/*
* To be called from `RCTMountingManager`.
*/
void notifyObserversMountingTransactionWillMount(
facebook::react::MountingTransactionMetadata const &metadata) const;
void notifyObserversMountingTransactionDidMount(
facebook::react::MountingTransactionMetadata const &metadata) const;
private:
facebook::better::map<
facebook::react::SurfaceId,
facebook::better::set<RCTComponentViewDescriptor>>
registry_;
};
@@ -0,0 +1,72 @@
/*
* 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 "RCTMountingTransactionObserverCoordinator.h"
#import "RCTMountingTransactionObserving.h"
using namespace facebook::react;
void RCTMountingTransactionObserverCoordinator::registerViewComponentDescriptor(
RCTComponentViewDescriptor const &componentViewDescriptor,
SurfaceId surfaceId)
{
if (!componentViewDescriptor.observesMountingTransactionWillMount &&
!componentViewDescriptor.observesMountingTransactionDidMount) {
return;
}
auto &surfaceRegistry = registry_[surfaceId];
surfaceRegistry.insert(componentViewDescriptor);
}
void RCTMountingTransactionObserverCoordinator::unregisterViewComponentDescriptor(
RCTComponentViewDescriptor const &componentViewDescriptor,
SurfaceId surfaceId)
{
if (!componentViewDescriptor.observesMountingTransactionWillMount &&
!componentViewDescriptor.observesMountingTransactionDidMount) {
return;
}
auto &surfaceRegistry = registry_[surfaceId];
surfaceRegistry.erase(componentViewDescriptor);
}
void RCTMountingTransactionObserverCoordinator::notifyObserversMountingTransactionWillMount(
MountingTransactionMetadata const &metadata) const
{
auto surfaceId = metadata.surfaceId;
auto surfaceRegistryIterator = registry_.find(surfaceId);
if (surfaceRegistryIterator == registry_.end()) {
return;
}
auto &surfaceRegistry = surfaceRegistryIterator->second;
for (auto const &componentViewDescriptor : surfaceRegistry) {
if (componentViewDescriptor.observesMountingTransactionWillMount) {
[(id<RCTMountingTransactionObserving>)componentViewDescriptor.view
mountingTransactionWillMountWithMetadata:metadata];
}
}
}
void RCTMountingTransactionObserverCoordinator::notifyObserversMountingTransactionDidMount(
MountingTransactionMetadata const &metadata) const
{
auto surfaceId = metadata.surfaceId;
auto surfaceRegistryIterator = registry_.find(surfaceId);
if (surfaceRegistryIterator == registry_.end()) {
return;
}
auto &surfaceRegistry = surfaceRegistryIterator->second;
for (auto const &componentViewDescriptor : surfaceRegistry) {
if (componentViewDescriptor.observesMountingTransactionDidMount) {
[(id<RCTMountingTransactionObserving>)componentViewDescriptor.view
mountingTransactionDidMountWithMetadata:metadata];
}
}
}
@@ -16,7 +16,7 @@ namespace react {
/*
* Contains all (meta)information related to a MountingTransaction except a list
* of mutation instructions.
* The class is meant to be used when a cosumer should not have access to all
* The class is meant to be used when a consumer should not have access to all
* information about the transaction (incapsulation) but still needs to observe
* it to produce some side-effects.
*/