/* * 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 "RCTComponentViewRegistry.h" #import #import #import "RCTImageComponentView.h" #import "RCTParagraphComponentView.h" #import "RCTViewComponentView.h" #import using namespace facebook::react; #define LEGACY_UIMANAGER_INTEGRATION_ENABLED 1 #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED #import #import /** * Warning: This is a total hack and temporary solution. * Unless we have a pure Fabric-based implementation of UIManager commands * delivery pipeline, we have to leverage existing infra. This code tricks * legacy UIManager by registering all Fabric-managed views in it, * hence existing command-delivery infra can reach "foreign" views using * the old pipeline. */ @interface RCTUIManager () - (NSMutableDictionary *)viewRegistry; @end @interface RCTUIManager (Hack) + (void)registerView:(UIView *)view; + (void)unregisterView:(UIView *)view; @end @implementation RCTUIManager (Hack) + (void)registerView:(UIView *)view { if (!view) { return; } RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager]; view.reactTag = @(view.tag); [uiManager.viewRegistry setObject:view forKey:@(view.tag)]; } + (void)unregisterView:(UIView *)view { if (!view) { return; } RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager]; view.reactTag = nil; [uiManager.viewRegistry removeObjectForKey:@(view.tag)]; } @end #endif const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024; @implementation RCTComponentViewRegistry { better::map _registry; better::map> _recyclePool; } - (instancetype)init { if (self = [super init]) { _componentViewFactory = [RCTComponentViewFactory standardComponentViewFactory]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleApplicationDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // Calling this a bit later, when the main thread is probably idle while JavaScript thread is busy. [self preallocateViewComponents]; }); } return self; } - (void)preallocateViewComponents { // This data is based on empirical evidence which should represent the reality pretty well. // Regular `` has magnitude equals to `1` by definition. std::vector> componentMagnitudes = { {[RCTViewComponentView componentDescriptorProvider].handle, 1}, {[RCTImageComponentView componentDescriptorProvider].handle, 0.3}, {[RCTParagraphComponentView componentDescriptorProvider].handle, 0.3}, }; // `complexity` represents the complexity of a typical surface in a number of `` components (with Flattening // enabled). float complexity = 100; // The whole process should not take more than 10ms in the worst case, so there is no need to split it up. for (const auto &componentMagnitude : componentMagnitudes) { for (int i = 0; i < complexity * componentMagnitude.second; i++) { [self optimisticallyCreateComponentViewWithComponentHandle:componentMagnitude.first]; } } } - (RCTComponentViewDescriptor)dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle tag:(Tag)tag { RCTAssertMainQueue(); RCTAssert( _registry.find(tag) == _registry.end(), @"RCTComponentViewRegistry: Attempt to dequeue already registered component."); auto componentViewDescriptor = [self _dequeueComponentViewWithComponentHandle:componentHandle]; componentViewDescriptor.view.tag = tag; _registry.insert({tag, componentViewDescriptor}); #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED [RCTUIManager registerView:componentViewDescriptor.view]; #endif return componentViewDescriptor; } - (void)enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle tag:(Tag)tag componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor { RCTAssertMainQueue(); RCTAssert( _registry.find(tag) != _registry.end(), @"RCTComponentViewRegistry: Attempt to enqueue unregistered component."); #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED [RCTUIManager unregisterView:componentViewDescriptor.view]; #endif _registry.erase(tag); componentViewDescriptor.view.tag = 0; [self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor]; } - (void)optimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle { RCTAssertMainQueue(); [self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:[self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]]; } - (RCTComponentViewDescriptor const &)componentViewDescriptorWithTag:(Tag)tag { RCTAssertMainQueue(); auto iterator = _registry.find(tag); RCTAssert(iterator != _registry.end(), @"RCTComponentViewRegistry: Attempt to query unregistered component."); return iterator->second; } - (nullable UIView *)findComponentViewWithTag:(Tag)tag { RCTAssertMainQueue(); auto iterator = _registry.find(tag); if (iterator == _registry.end()) { return nil; } return iterator->second.view; } - (RCTComponentViewDescriptor)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle { RCTAssertMainQueue(); auto &recycledViews = _recyclePool[componentHandle]; if (recycledViews.size() == 0) { return [self.componentViewFactory createComponentViewWithComponentHandle:componentHandle]; } auto componentViewDescriptor = recycledViews.back(); recycledViews.pop_back(); return componentViewDescriptor; } - (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor { RCTAssertMainQueue(); auto &recycledViews = _recyclePool[componentHandle]; if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) { return; } [componentViewDescriptor.view prepareForRecycle]; recycledViews.push_back(componentViewDescriptor); } - (void)handleApplicationDidReceiveMemoryWarningNotification { _recyclePool.clear(); } @end