RCTComponentViewRegistry.mm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTComponentViewRegistry.h"
  8. #import <Foundation/NSMapTable.h>
  9. #import <React/RCTAssert.h>
  10. #import "RCTImageComponentView.h"
  11. #import "RCTParagraphComponentView.h"
  12. #import "RCTViewComponentView.h"
  13. #import <better/map.h>
  14. using namespace facebook::react;
  15. #define LEGACY_UIMANAGER_INTEGRATION_ENABLED 1
  16. #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
  17. #import <React/RCTBridge+Private.h>
  18. #import <React/RCTUIManager.h>
  19. /**
  20. * Warning: This is a total hack and temporary solution.
  21. * Unless we have a pure Fabric-based implementation of UIManager commands
  22. * delivery pipeline, we have to leverage existing infra. This code tricks
  23. * legacy UIManager by registering all Fabric-managed views in it,
  24. * hence existing command-delivery infra can reach "foreign" views using
  25. * the old pipeline.
  26. */
  27. @interface RCTUIManager ()
  28. - (NSMutableDictionary<NSNumber *, UIView *> *)viewRegistry;
  29. @end
  30. @interface RCTUIManager (Hack)
  31. + (void)registerView:(UIView *)view;
  32. + (void)unregisterView:(UIView *)view;
  33. @end
  34. @implementation RCTUIManager (Hack)
  35. + (void)registerView:(UIView *)view
  36. {
  37. if (!view) {
  38. return;
  39. }
  40. RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager];
  41. view.reactTag = @(view.tag);
  42. [uiManager.viewRegistry setObject:view forKey:@(view.tag)];
  43. }
  44. + (void)unregisterView:(UIView *)view
  45. {
  46. if (!view) {
  47. return;
  48. }
  49. RCTUIManager *uiManager = [[RCTBridge currentBridge] uiManager];
  50. view.reactTag = nil;
  51. [uiManager.viewRegistry removeObjectForKey:@(view.tag)];
  52. }
  53. @end
  54. #endif
  55. const NSInteger RCTComponentViewRegistryRecyclePoolMaxSize = 1024;
  56. @implementation RCTComponentViewRegistry {
  57. better::map<Tag, RCTComponentViewDescriptor> _registry;
  58. better::map<ComponentHandle, std::vector<RCTComponentViewDescriptor>> _recyclePool;
  59. }
  60. - (instancetype)init
  61. {
  62. if (self = [super init]) {
  63. _componentViewFactory = [RCTComponentViewFactory standardComponentViewFactory];
  64. [[NSNotificationCenter defaultCenter] addObserver:self
  65. selector:@selector(handleApplicationDidReceiveMemoryWarningNotification)
  66. name:UIApplicationDidReceiveMemoryWarningNotification
  67. object:nil];
  68. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  69. // Calling this a bit later, when the main thread is probably idle while JavaScript thread is busy.
  70. [self preallocateViewComponents];
  71. });
  72. }
  73. return self;
  74. }
  75. - (void)preallocateViewComponents
  76. {
  77. // This data is based on empirical evidence which should represent the reality pretty well.
  78. // Regular `<View>` has magnitude equals to `1` by definition.
  79. std::vector<std::pair<ComponentHandle, float>> componentMagnitudes = {
  80. {[RCTViewComponentView componentDescriptorProvider].handle, 1},
  81. {[RCTImageComponentView componentDescriptorProvider].handle, 0.3},
  82. {[RCTParagraphComponentView componentDescriptorProvider].handle, 0.3},
  83. };
  84. // `complexity` represents the complexity of a typical surface in a number of `<View>` components (with Flattening
  85. // enabled).
  86. float complexity = 100;
  87. // The whole process should not take more than 10ms in the worst case, so there is no need to split it up.
  88. for (const auto &componentMagnitude : componentMagnitudes) {
  89. for (int i = 0; i < complexity * componentMagnitude.second; i++) {
  90. [self optimisticallyCreateComponentViewWithComponentHandle:componentMagnitude.first];
  91. }
  92. }
  93. }
  94. - (RCTComponentViewDescriptor)dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle tag:(Tag)tag
  95. {
  96. RCTAssertMainQueue();
  97. RCTAssert(
  98. _registry.find(tag) == _registry.end(),
  99. @"RCTComponentViewRegistry: Attempt to dequeue already registered component.");
  100. auto componentViewDescriptor = [self _dequeueComponentViewWithComponentHandle:componentHandle];
  101. componentViewDescriptor.view.tag = tag;
  102. _registry.insert({tag, componentViewDescriptor});
  103. #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
  104. [RCTUIManager registerView:componentViewDescriptor.view];
  105. #endif
  106. return componentViewDescriptor;
  107. }
  108. - (void)enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
  109. tag:(Tag)tag
  110. componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor
  111. {
  112. RCTAssertMainQueue();
  113. RCTAssert(
  114. _registry.find(tag) != _registry.end(), @"RCTComponentViewRegistry: Attempt to enqueue unregistered component.");
  115. #ifdef LEGACY_UIMANAGER_INTEGRATION_ENABLED
  116. [RCTUIManager unregisterView:componentViewDescriptor.view];
  117. #endif
  118. _registry.erase(tag);
  119. componentViewDescriptor.view.tag = 0;
  120. [self _enqueueComponentViewWithComponentHandle:componentHandle componentViewDescriptor:componentViewDescriptor];
  121. }
  122. - (void)optimisticallyCreateComponentViewWithComponentHandle:(ComponentHandle)componentHandle
  123. {
  124. RCTAssertMainQueue();
  125. [self _enqueueComponentViewWithComponentHandle:componentHandle
  126. componentViewDescriptor:[self.componentViewFactory
  127. createComponentViewWithComponentHandle:componentHandle]];
  128. }
  129. - (RCTComponentViewDescriptor const &)componentViewDescriptorWithTag:(Tag)tag
  130. {
  131. RCTAssertMainQueue();
  132. auto iterator = _registry.find(tag);
  133. RCTAssert(iterator != _registry.end(), @"RCTComponentViewRegistry: Attempt to query unregistered component.");
  134. return iterator->second;
  135. }
  136. - (nullable UIView<RCTComponentViewProtocol> *)findComponentViewWithTag:(Tag)tag
  137. {
  138. RCTAssertMainQueue();
  139. auto iterator = _registry.find(tag);
  140. if (iterator == _registry.end()) {
  141. return nil;
  142. }
  143. return iterator->second.view;
  144. }
  145. - (RCTComponentViewDescriptor)_dequeueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
  146. {
  147. RCTAssertMainQueue();
  148. auto &recycledViews = _recyclePool[componentHandle];
  149. if (recycledViews.size() == 0) {
  150. return [self.componentViewFactory createComponentViewWithComponentHandle:componentHandle];
  151. }
  152. auto componentViewDescriptor = recycledViews.back();
  153. recycledViews.pop_back();
  154. return componentViewDescriptor;
  155. }
  156. - (void)_enqueueComponentViewWithComponentHandle:(ComponentHandle)componentHandle
  157. componentViewDescriptor:(RCTComponentViewDescriptor)componentViewDescriptor
  158. {
  159. RCTAssertMainQueue();
  160. auto &recycledViews = _recyclePool[componentHandle];
  161. if (recycledViews.size() > RCTComponentViewRegistryRecyclePoolMaxSize) {
  162. return;
  163. }
  164. [componentViewDescriptor.view prepareForRecycle];
  165. recycledViews.push_back(componentViewDescriptor);
  166. }
  167. - (void)handleApplicationDidReceiveMemoryWarningNotification
  168. {
  169. _recyclePool.clear();
  170. }
  171. @end