From eae8830b6595dbffdff7ef8bd91cb3c59a13b250 Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 21 Aug 2018 15:51:20 +0200 Subject: [PATCH] Use uiManagerDidPerformMounting to perform batch updates of ScreenConatiner on iOS (we should perhaps do sth similar in ScreenStack too). --- ios/RNSScreenContainer.h | 3 -- ios/RNSScreenContainer.m | 74 +++++++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/ios/RNSScreenContainer.h b/ios/RNSScreenContainer.h index c4e5de43..60c6583b 100644 --- a/ios/RNSScreenContainer.h +++ b/ios/RNSScreenContainer.h @@ -8,6 +8,3 @@ @interface RNSScreenContainerView : UIView @end - -@interface RNSScreenContainerManager : RCTViewManager -@end diff --git a/ios/RNSScreenContainer.m b/ios/RNSScreenContainer.m index 8376fc4a..258cff9d 100644 --- a/ios/RNSScreenContainer.m +++ b/ios/RNSScreenContainer.m @@ -2,27 +2,38 @@ #import "RNSScreen.h" #import +#import #import +@interface RNSScreenContainerManager : RCTViewManager + +- (void)markUpdated:(RNSScreenContainerView *)screen; + +@end + @interface RNSScreenContainerView () @property (nonatomic, retain) UIViewController *controller; @property (nonatomic, retain) NSMutableSet *activeScreens; @property (nonatomic, retain) NSMutableArray *reactSubviews; +- (void)updateConatiner; + @end @implementation RNSScreenContainerView { BOOL _needUpdate; + __weak RNSScreenContainerManager *_manager; } -- (instancetype)init +- (instancetype)initWithManager:(RNSScreenContainerManager *)manager { if (self = [super init]) { _activeScreens = [NSMutableSet new]; _reactSubviews = [NSMutableArray new]; _controller = [[UIViewController alloc] init]; _needUpdate = NO; + _manager = manager; [self addSubview:_controller.view]; } return self; @@ -36,12 +47,7 @@ // there is a chance it is not the correct way to do that. if (!_needUpdate) { _needUpdate = YES; - RCTExecuteOnUIManagerQueue(^{ - RCTExecuteOnMainQueue(^{ - _needUpdate = NO; - [self updateContainer]; - }); - }); + [_manager markUpdated:self]; } } @@ -80,6 +86,7 @@ - (void)updateContainer { + _needUpdate = NO; BOOL activeScreenChanged = NO; // remove screens that are no longer active NSMutableSet *orphaned = [NSMutableSet setWithSet:_activeScreens]; @@ -130,13 +137,62 @@ @end -@implementation RNSScreenContainerManager +@implementation RNSScreenContainerManager { + NSMutableArray *_markedContainers; +} RCT_EXPORT_MODULE() - (UIView *)view { - return [[RNSScreenContainerView alloc] init]; + if (!_markedContainers) { + _markedContainers = [NSMutableArray new]; + } + return [[RNSScreenContainerView alloc] initWithManager:self]; +} + +- (void)markUpdated:(RNSScreenContainerView *)screen +{ + RCTAssertMainQueue(); + @synchronized(self) { + // we need to synchronize write operations so that in didPerformMounting we can reliably + // tell if _markedCOntainers is empty or not + [_markedContainers addObject:screen]; + } +} + +#pragma mark - RCTUIManagerObserver + +- (void)setBridge:(RCTBridge *)bridge +{ + [super setBridge:bridge]; + [self.bridge.uiManager.observerCoordinator addObserver:self]; +} + +- (void)invalidate +{ + [self.bridge.uiManager.observerCoordinator removeObserver:self]; +} + +- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager +{ + @synchronized(self) { + if ([_markedContainers count] == 0) { + // we return early if there are no updated containers. This check needs to be + // synchronized as UIThread can modify _markedContainers array + return; + } + } + RCTExecuteOnMainQueue(^{ + for (RNSScreenContainerView *screen in _markedContainers) { + [screen updateContainer]; + } + @synchronized(self) { + // we only synchronize write operations and not reading in UIThread as UIThread + // is the only thread that changes _markedContainers + [_markedContainers removeAllObjects]; + } + }); } @end