diff --git a/ComponentKit/Core/CKComponentLifecycleManager.mm b/ComponentKit/Core/CKComponentLifecycleManager.mm index 9f87337ac..1cd61ae56 100644 --- a/ComponentKit/Core/CKComponentLifecycleManager.mm +++ b/ComponentKit/Core/CKComponentLifecycleManager.mm @@ -16,6 +16,7 @@ #import "CKComponent.h" #import "CKComponentInternal.h" +#import "CKComponentDebugController.h" #import "CKComponentLayout.h" #import "CKComponentLifecycleManagerAsynchronousUpdateHandler.h" #import "CKComponentMemoizer.h" @@ -37,6 +38,9 @@ .root = nil, }; +@interface CKComponentLifecycleManager () +@end + @implementation CKComponentLifecycleManager { UIView *_mountedView; @@ -62,6 +66,7 @@ - (instancetype)initWithComponentProvider:(Class)componentP if (self = [super init]) { _componentProvider = componentProvider; _sizeRangeProvider = sizeRangeProvider; + [CKComponentDebugController registerReflowListener:self]; } return self; } @@ -220,4 +225,9 @@ - (void)componentScopeHandleWithIdentifier:(int32_t)globalIdentifier return _state; } +- (void)didReceiveReflowComponentsRequest +{ + [self prepareForUpdateWithModel:_state.model constrainedSize:_state.constrainedSize context:_state.context]; +} + @end diff --git a/ComponentKit/Debug/CKComponentDebugController.h b/ComponentKit/Debug/CKComponentDebugController.h index 7c44cbe34..1d2b96034 100644 --- a/ComponentKit/Debug/CKComponentDebugController.h +++ b/ComponentKit/Debug/CKComponentDebugController.h @@ -14,7 +14,10 @@ #import @class CKComponent; -@class UIView; + +@protocol CKComponentDebugReflowListener +- (void)didReceiveReflowComponentsRequest; +@end /** CKComponentDebugController exposes the functionality needed by the lldb helpers to control the debug behavior for @@ -27,16 +30,22 @@ /** Setting the debug mode enables the injection of debug configuration into the component. */ -+ (void)setDebugMode:(BOOL)debugMode NS_EXTENSION_UNAVAILABLE("Recursively reflows components using -[UIApplication keyWindow]"); ++ (void)setDebugMode:(BOOL)debugMode; /** Components are an immutable construct. Whenever we make changes to the parameters on which the components depended, the changes won't be reflected in the component hierarchy until we explicitly cause a reflow/update. A reflow - essentially rebuilds the component hierarchy and mounts it back on the view. + essentially rebuilds the component hierarchy and remounts on the attached view, if any. - This is particularly used in reflowing the component hierarchy when we set the debug mode. + This is automatically triggered when changing debug mode, to ensure that debug views are added or removed. + */ ++ (void)reflowComponents; + +/** + Registers an object that will be notified when +reflowComponents is called. The listener is weakly held and will + be messaged on the main thread. */ -+ (void)reflowComponents NS_EXTENSION_UNAVAILABLE("Recursively reflows components using -[UIApplication keyWindow]"); ++ (void)registerReflowListener:(id)listener; @end diff --git a/ComponentKit/Debug/CKComponentDebugController.mm b/ComponentKit/Debug/CKComponentDebugController.mm index ff006d946..a45e02a40 100644 --- a/ComponentKit/Debug/CKComponentDebugController.mm +++ b/ComponentKit/Debug/CKComponentDebugController.mm @@ -12,6 +12,8 @@ #import +#import + #import "CKComponent.h" #import "CKComponentAnimation.h" #import "CKComponentHostingView.h" @@ -109,9 +111,6 @@ + (BOOL)debugMode return context; // no need for a debug view if the component has a view. } - static CK::StaticMutex l = CK_MUTEX_INITIALIZER; - CK::StaticMutexLocker lock(l); - // Avoid the static destructor fiasco, use a pointer: static std::unordered_map *debugViewConfigurations = new std::unordered_map(); @@ -133,46 +132,35 @@ + (BOOL)debugMode #pragma mark - Synchronous Reflow +static NSHashTable> *reflowListeners; +static std::mutex reflowMutex; + + (void)reflowComponents { if (![NSThread isMainThread]) { dispatch_async(dispatch_get_main_queue(), ^{ [self reflowComponents]; }); - } else { - UIWindow *window = [[UIApplication sharedApplication] keyWindow]; - CKRecursiveComponentReflow(window); + return; } -} - -+ (void)reflowComponentsForView:(UIView *)view searchUpwards:(BOOL)upwards -{ - if (upwards) { - while (view && ![view isKindOfClass:[CKComponentRootView class]]) { - view = view.superview; - } + NSArray> *copiedListeners; + { + std::lock_guard l(reflowMutex); + copiedListeners = [reflowListeners allObjects]; } - if (view) { - CKRecursiveComponentReflow(view); + for (id listener in copiedListeners) { + [listener didReceiveReflowComponentsRequest]; } } -static void CKRecursiveComponentReflow(UIView *view) ++ (void)registerReflowListener:(id)listener { - if (view.ck_componentLifecycleManager) { - CKComponentLifecycleManager *lifecycleManager = view.ck_componentLifecycleManager; - CKComponentLifecycleManagerState oldState = [lifecycleManager state]; - CKComponentLifecycleManagerState state = - [lifecycleManager prepareForUpdateWithModel:oldState.model - constrainedSize:oldState.constrainedSize - context:oldState.context]; - [lifecycleManager updateWithState:state]; - } else if ([view.superview isKindOfClass:[CKComponentHostingView class]]) { - CKComponentHostingView *hostingView = (CKComponentHostingView *)view.superview; - [hostingView setNeedsLayout]; - } else { - for (UIView *subview in view.subviews) { - CKRecursiveComponentReflow(subview); - } + if (listener == nil) { + return; + } + std::lock_guard l(reflowMutex); + if (reflowListeners == nil) { + reflowListeners = [NSHashTable weakObjectsHashTable]; } + [reflowListeners addObject:listener]; } @end diff --git a/ComponentKit/HostingView/CKComponentHostingView.mm b/ComponentKit/HostingView/CKComponentHostingView.mm index 5d271ef03..86af1d2d1 100644 --- a/ComponentKit/HostingView/CKComponentHostingView.mm +++ b/ComponentKit/HostingView/CKComponentHostingView.mm @@ -15,6 +15,7 @@ #import #import "CKComponentAnimation.h" +#import "CKComponentDebugController.h" #import "CKComponentHostingViewDelegate.h" #import "CKComponentLayout.h" #import "CKComponentRootView.h" @@ -33,7 +34,7 @@ }; }; -@interface CKComponentHostingView () +@interface CKComponentHostingView () { Class _componentProvider; id _sizeRangeProvider; @@ -75,6 +76,8 @@ - (instancetype)initWithComponentProvider:(Class)componentP _componentNeedsUpdate = YES; _requestedUpdateMode = CKUpdateModeSynchronous; + + [CKComponentDebugController registerReflowListener:self]; } return self; } @@ -150,6 +153,13 @@ - (void)componentScopeHandleWithIdentifier:(CKComponentScopeHandleIdentifier)glo [self _setNeedsUpdateWithMode:mode]; } +#pragma mark - CKComponentDebugController + +- (void)didReceiveReflowComponentsRequest +{ + [self _setNeedsUpdateWithMode:CKUpdateModeAsynchronous]; +} + #pragma mark - Private - (void)_setNeedsUpdateWithMode:(CKUpdateMode)mode diff --git a/ComponentKit/TransactionalDataSources/Common/CKTransactionalComponentDataSource.mm b/ComponentKit/TransactionalDataSources/Common/CKTransactionalComponentDataSource.mm index 490e6d72a..6e3310aa0 100644 --- a/ComponentKit/TransactionalDataSources/Common/CKTransactionalComponentDataSource.mm +++ b/ComponentKit/TransactionalDataSources/Common/CKTransactionalComponentDataSource.mm @@ -12,6 +12,7 @@ #import "CKTransactionalComponentDataSourceInternal.h" #import "CKAssert.h" +#import "CKComponentDebugController.h" #import "CKComponentScopeRoot.h" #import "CKTransactionalComponentDataSourceChange.h" #import "CKTransactionalComponentDataSourceChangesetModification.h" @@ -35,7 +36,7 @@ - (instancetype)initWithModification:(id +@interface CKTransactionalComponentDataSource () { CKTransactionalComponentDataSourceState *_state; CKTransactionalComponentDataSourceListenerAnnouncer *_announcer; @@ -60,6 +61,7 @@ - (instancetype)initWithConfiguration:(CKTransactionalComponentDataSourceConfigu _workQueue = dispatch_queue_create("org.componentkit.CKTransactionalComponentDataSource", DISPATCH_QUEUE_SERIAL); _pendingAsynchronousModifications = [NSMutableArray array]; _workThreadOverride = configuration.workThreadOverride; + [CKComponentDebugController registerReflowListener:self]; } return self; } @@ -163,6 +165,13 @@ - (void)componentScopeHandleWithIdentifier:(CKComponentScopeHandleIdentifier)glo } } +#pragma mark - CKComponentDebugReflowListener + +- (void)didReceiveReflowComponentsRequest +{ + [self reloadWithMode:CKUpdateModeAsynchronous userInfo:nil]; +} + #pragma mark - Internal - (void)_enqueueModification:(id)modification