diff --git a/renderer/native/ios/renderer/HippyComponentMap.h b/renderer/native/ios/renderer/HippyComponentMap.h index b47f911b227..2b8ac6224ee 100644 --- a/renderer/native/ios/renderer/HippyComponentMap.h +++ b/renderer/native/ios/renderer/HippyComponentMap.h @@ -34,15 +34,31 @@ class RootNode; NS_ASSUME_NONNULL_BEGIN +typedef NS_ENUM(NSUInteger, HippyComponentReferenceType) { + HippyComponentReferenceTypeStrong, + HippyComponentReferenceTypeWeak, +}; + @interface HippyComponentMap : NSObject +/// Whether all recorded elements are strongly referenced, +/// +/// Attention, Attention, Attention: +/// All UI views are weakly referenced! +/// All Shadowviews are strongly referenced! +@property (nonatomic, assign, readonly) BOOL isStrongHoldAllComponents; + +/// Whether access is required from the main thread @property(nonatomic, assign) BOOL requireInMainThread; +/// Init Method +- (instancetype)initWithComponentsReferencedType:(HippyComponentReferenceType)type; +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + - (void)addRootComponent:(id)component rootNode:(std::weak_ptr)rootNode - forTag:(NSNumber *)tag - strongHoldComponents:(BOOL)shouldStrongHoldAllComponents; - + forTag:(NSNumber *)tag; - (void)removeRootComponentWithTag:(NSNumber *)tag; @@ -62,7 +78,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumber *)rootTag; -- (NSMutableDictionary> *)componentsForRootTag:(NSNumber *)tag; +- (NSDictionary> *)componentsForRootTag:(NSNumber *)tag; - (__kindof id)componentForTag:(NSNumber *)componentTag onRootTag:(NSNumber *)tag; diff --git a/renderer/native/ios/renderer/HippyComponentMap.mm b/renderer/native/ios/renderer/HippyComponentMap.mm index ce89fbb7591..6ef58deb851 100644 --- a/renderer/native/ios/renderer/HippyComponentMap.mm +++ b/renderer/native/ios/renderer/HippyComponentMap.mm @@ -36,9 +36,10 @@ @interface HippyComponentMap () { @implementation HippyComponentMap -- (instancetype)init { +- (instancetype)initWithComponentsReferencedType:(HippyComponentReferenceType)type { self = [super init]; if (self) { + _isStrongHoldAllComponents = (HippyComponentReferenceTypeStrong == type); _rootComponentsMap = [NSMapTable strongToWeakObjectsMapTable]; _componentsMap = [NSMutableDictionary dictionary]; _rootNodesMap.reserve(8); @@ -52,13 +53,12 @@ - (BOOL)threadCheck { - (void)addRootComponent:(id)component rootNode:(std::weak_ptr)rootNode - forTag:(NSNumber *)tag - strongHoldComponents:(BOOL)shouldStrongHoldAllComponents { + forTag:(NSNumber *)tag { NSAssert(component && tag, @"component &&tag must not be null in method %@", NSStringFromSelector(_cmd)); NSAssert([self threadCheck], @"%@ method needs run in main thread", NSStringFromSelector(_cmd)); if (component && tag && ![_componentsMap objectForKey:tag]) { id dic = nil; - if (shouldStrongHoldAllComponents) { + if (_isStrongHoldAllComponents) { dic = [NSMutableDictionary dictionary]; } else { dic = [NSMapTable strongToWeakObjectsMapTable]; @@ -128,12 +128,16 @@ - (void)removeComponentByComponentTag:(NSNumber *)componentTag onRootTag:(NSNumb } } -- (NSMutableDictionary> *)componentsForRootTag:(NSNumber *)tag { +- (NSDictionary> *)componentsForRootTag:(NSNumber *)tag { NSAssert(tag, @"tag must not be null in method %@", NSStringFromSelector(_cmd)); NSAssert([self threadCheck], @"%@ method needs run in main thread", NSStringFromSelector(_cmd)); if (tag) { id map = [_componentsMap objectForKey:tag]; - return map; + if (_isStrongHoldAllComponents) { + return map; + } else { + return ((NSMapTable *)map).dictionaryRepresentation; + } } return nil; } diff --git a/renderer/native/ios/renderer/HippyUIManager.mm b/renderer/native/ios/renderer/HippyUIManager.mm index 5ab42e76c40..1934390a705 100644 --- a/renderer/native/ios/renderer/HippyUIManager.mm +++ b/renderer/native/ios/renderer/HippyUIManager.mm @@ -214,8 +214,8 @@ - (void)dealloc { } - (void)initContext { - _shadowViewRegistry = [[HippyComponentMap alloc] init]; - _viewRegistry = [[HippyComponentMap alloc] init]; + _shadowViewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeStrong]; + _viewRegistry = [[HippyComponentMap alloc] initWithComponentsReferencedType:HippyComponentReferenceTypeWeak]; _viewRegistry.requireInMainThread = YES; _pendingUIBlocks = [NSMutableArray new]; _componentTransactionListeners = [NSHashTable weakObjectsHashTable]; @@ -263,20 +263,6 @@ - (void)domNodeForComponentTag:(int32_t)componentTag } } -- (HippyComponentMap *)renderObjectRegistry { - if (!_shadowViewRegistry) { - _shadowViewRegistry = [[HippyComponentMap alloc] init]; - } - return _shadowViewRegistry; - } - -- (HippyComponentMap *)viewRegistry { - if (!_viewRegistry) { - _viewRegistry = [[HippyComponentMap alloc] init]; - } - return _viewRegistry; -} - - (UIView *)viewForHippyTag:(NSNumber *)hippyTag onRootTag:(NSNumber *)rootTag { AssertMainQueue(); return [_viewRegistry componentForTag:hippyTag onRootTag:rootTag]; @@ -323,7 +309,7 @@ - (void)registerRootView:(UIView *)rootView asRootNode:(std::weak_ptr) NSAssert(![_viewRegistry containRootComponentWithTag:componentTag], @"RootView Tag already exists. Added %@ twice", componentTag); #endif // Register view - [_viewRegistry addRootComponent:rootView rootNode:rootNode forTag:componentTag strongHoldComponents:NO]; + [_viewRegistry addRootComponent:rootView rootNode:rootNode forTag:componentTag]; [rootView addObserver:self forKeyPath:@"frame" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:NULL]; rootView.renderManager = [self renderManager]; @@ -347,7 +333,7 @@ - (void)registerRootView:(UIView *)rootView asRootNode:(std::weak_ptr) renderObject.viewName = rootViewClassName; renderObject.rootNode = rootNode; renderObject.domNode = rootNode; - [strongSelf->_shadowViewRegistry addRootComponent:renderObject rootNode:rootNode forTag:componentTag strongHoldComponents:YES]; + [strongSelf->_shadowViewRegistry addRootComponent:renderObject rootNode:rootNode forTag:componentTag]; NSDictionary *userInfo = @{ NativeRenderUIManagerRootViewTagKey: componentTag, NativeRenderUIManagerKey: strongSelf}; [[NSNotificationCenter defaultCenter] postNotificationName:NativeRenderUIManagerDidRegisterRootViewNotification @@ -462,14 +448,15 @@ - (void)setFrame:(CGRect)frame forRootView:(UIView *)view { */ - (void)purgeChildren:(NSArray> *)children onRootTag:(NSNumber *)rootTag - fromRegistry:(NSMutableDictionary> *)registry { + fromRegistry:(HippyComponentMap *)registryMap { + NSDictionary *currentRegistry = [registryMap componentsForRootTag:rootTag]; for (id child in children) { - NativeRenderTraverseViewNodes(registry[child.hippyTag], ^(id subview) { + NativeRenderTraverseViewNodes(currentRegistry[child.hippyTag], ^(id subview) { NSAssert(![subview isHippyRootView], @"Root views should not be unregistered"); if ([subview respondsToSelector:@selector(invalidate)]) { [subview performSelector:@selector(invalidate)]; } - [registry removeObjectForKey:subview.hippyTag]; + [registryMap removeComponent:subview forRootTag:rootTag]; }); } } @@ -588,8 +575,10 @@ - (UIView *)createViewRecursiveFromRenderObjectWithNOLock:(HippyShadowView *)sha NSMutableSet *applierBlocks = [NSMutableSet set]; [shadowView amendLayoutBeforeMount:applierBlocks]; if (applierBlocks.count) { - NSDictionary *viewRegistry = [self.viewRegistry componentsForRootTag:shadowView.rootTag]; for (NativeRenderApplierBlock block in applierBlocks) { + // Note: viewRegistry may be modified in the block, and it may be stored internally as NSMapTable + // so to ensure that it is up-to-date, it can only be retrieved each time. + NSDictionary *viewRegistry = [self.viewRegistry componentsForRootTag:shadowView.rootTag]; block(viewRegistry, view); } } @@ -739,9 +728,11 @@ - (void)flushUIBlocksOnRootNode:(std::weak_ptr)rootNode { __strong __typeof(weakSelf)strongSelf = weakSelf; if (strongSelf) { TDF_PERF_LOG("flushUIBlocksOnRootNode on main thread(random id:%u)",rand); - NSDictionary *viewReg = [strongSelf.viewRegistry componentsForRootTag:@(rootTag)]; for (HippyViewManagerUIBlock block in previousPendingUIBlocks) { @try { + // Note: viewRegistry may be modified in the block, and it may be stored internally as NSMapTable + // so to ensure that it is up-to-date, it can only be retrieved each time. + NSDictionary* viewReg = [strongSelf.viewRegistry componentsForRootTag:@(rootTag)]; block(strongSelf, viewReg); } @catch (NSException *exception) { HippyLogError(@"Exception thrown while executing UI block: %@", exception); @@ -843,6 +834,9 @@ - (void)createRenderNodes:(std::vector> &&)nodes }]; } }]; + [self addUIBlock:^(HippyUIManager *uiManager, NSDictionary *viewRegistry) { + HippyLogInfo(@"Created views: %lu, full registry: %lu", (unsigned long)tempCreatedViews.count, viewRegistry.count); + }]; } - (void)updateRenderNodes:(std::vector>&&)nodes @@ -891,7 +885,7 @@ - (void)deleteRenderNodesIds:(std::vector> &&)no #endif std::lock_guard lock([self renderQueueLock]); NSNumber *rootTag = @(strongRootNode->GetId()); - NSMutableDictionary *currentRegistry = [_shadowViewRegistry componentsForRootTag:rootTag]; + NSDictionary *currentRegistry = [_shadowViewRegistry componentsForRootTag:rootTag]; for (auto dom_node : nodes) { int32_t tag = dom_node->GetRenderInfo().id; @@ -899,7 +893,7 @@ - (void)deleteRenderNodesIds:(std::vector> &&)no [renderObject dirtyPropagation:NativeRenderUpdateLifecycleLayoutDirtied]; if (renderObject) { [renderObject removeFromHippySuperview]; - [self purgeChildren:@[renderObject] onRootTag:rootTag fromRegistry:currentRegistry]; + [self purgeChildren:@[renderObject] onRootTag:rootTag fromRegistry:_shadowViewRegistry]; } } __weak HippyUIManager *weakSelf = self; @@ -924,8 +918,7 @@ - (void)deleteRenderNodesIds:(std::vector> &&)no [view removeFromHippySuperview]; [views addObject:view]; } - NSMutableDictionary *currentViewRegistry = [strongSelf.viewRegistry componentsForRootTag:rootTag]; - [strongSelf purgeChildren:views onRootTag:rootTag fromRegistry:currentViewRegistry]; + [strongSelf purgeChildren:views onRootTag:rootTag fromRegistry:strongSelf.viewRegistry]; for (UIView *view in parentViews) { [view clearSortedSubviews]; [view didUpdateHippySubviews];