ComponentKit/Core/Scope/CKComponentScopeRoot.mm (149 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKComponentScopeRoot.h" #include <atomic> #import <ComponentKit/CKInternalHelpers.h> #import <ComponentKit/CKRootTreeNode.h> #import "CKComponentProtocol.h" #import "CKComponentControllerProtocol.h" #import "CKThreadLocalComponentScope.h" typedef std::unordered_map<CKComponentPredicate, NSHashTable<id<CKComponentProtocol>> *> _CKRegisteredComponentsMap; typedef std::unordered_map<CKComponentControllerPredicate, NSHashTable<id<CKComponentControllerProtocol>> *> _CKRegisteredComponentControllerMap; @implementation CKComponentScopeRoot { std::unordered_set<CKComponentPredicate> _componentPredicates; std::unordered_set<CKComponentControllerPredicate> _componentControllerPredicates; _CKRegisteredComponentsMap _registeredComponents; _CKRegisteredComponentControllerMap _registeredComponentControllers; CKRootTreeNode _rootNode; } + (instancetype)rootWithListener:(id<CKComponentStateListener>)listener analyticsListener:(id<CKAnalyticsListener>)analyticsListener componentPredicates:(const std::unordered_set<CKComponentPredicate> &)componentPredicates componentControllerPredicates:(const std::unordered_set<CKComponentControllerPredicate> &)componentControllerPredicates { static std::atomic_int32_t nextGlobalIdentifier; return [[CKComponentScopeRoot alloc] initWithListener:listener analyticsListener:analyticsListener globalIdentifier:++nextGlobalIdentifier isEmpty:YES componentPredicates:componentPredicates componentControllerPredicates:componentControllerPredicates]; } - (instancetype)newRoot { return [[CKComponentScopeRoot alloc] initWithListener:_listener analyticsListener:_analyticsListener globalIdentifier:_globalIdentifier isEmpty:NO componentPredicates:_componentPredicates componentControllerPredicates:_componentControllerPredicates]; } - (instancetype)initWithListener:(id<CKComponentStateListener>)listener analyticsListener:(id<CKAnalyticsListener>)analyticsListener globalIdentifier:(CKComponentScopeRootIdentifier)globalIdentifier isEmpty:(BOOL)isEmpty componentPredicates:(const std::unordered_set<CKComponentPredicate> &)componentPredicates componentControllerPredicates:(const std::unordered_set<CKComponentControllerPredicate> &)componentControllerPredicates { if (self = [super init]) { auto const globalConfig = CKReadGlobalConfig(); _listener = listener; _analyticsListener = analyticsListener ?: globalConfig.defaultAnalyticsListener; _globalIdentifier = globalIdentifier; _componentPredicates = componentPredicates; _componentControllerPredicates = componentControllerPredicates; _isEmpty = isEmpty; } return self; } - (void)registerComponent:(id<CKComponentProtocol>)component { if (!component) { // Handle this gracefully so we don't have a bunch of nils being passed to predicates. return; } for (const auto &predicate : _componentPredicates) { if (predicate(component)) { auto hashTable = _registeredComponents[predicate]; if (!hashTable) { hashTable = [NSHashTable weakObjectsHashTable]; _registeredComponents[predicate] = hashTable; } RCWarn([hashTable containsObject:component] == NO, @"Double registration of component %@", component.className); [hashTable addObject:component]; } } } - (void)registerComponentController:(id<CKComponentControllerProtocol>)componentController { if (!componentController) { // As above, handle a nil component controller gracefully instead of passing through to predicate. return; } for (const auto &predicate : _componentControllerPredicates) { if (predicate(componentController)) { auto hashTable = _registeredComponentControllers[predicate]; if (!hashTable) { hashTable = [NSHashTable weakObjectsHashTable]; _registeredComponentControllers[predicate] = hashTable; } RCWarn([hashTable containsObject:componentController] == NO, @"Double registration of component controller %@", componentController.class); [hashTable addObject:componentController]; } } } - (void)enumerateComponentsMatchingPredicate:(CKComponentPredicate)predicate block:(CKComponentScopeEnumerator)block { if (!block) { RCFailAssert(@"Must be given a block to enumerate."); return; } RCAssert(_componentPredicates.find(predicate) != _componentPredicates.end(), @"Scope root must be initialized with predicate to enumerate."); const auto foundIter = _registeredComponents.find(predicate); if (foundIter != _registeredComponents.end()) { for (id<CKComponentProtocol> component in foundIter->second) { block(component); } } } - (CKCocoaCollectionAdapter<id<CKComponentProtocol>>)componentsMatchingPredicate:(CKComponentPredicate)predicate { RCCAssert(CK::Collection::contains(_componentPredicates, predicate), @"Scope root must be initialized with predicate to enumerate."); const auto componentsIt = _registeredComponents.find(predicate); const auto components = componentsIt != _registeredComponents.end() ? componentsIt->second : @[]; return CKCocoaCollectionAdapter<id<CKComponentProtocol>>(components); } - (void)enumerateComponentControllersMatchingPredicate:(CKComponentControllerPredicate)predicate block:(CKComponentControllerScopeEnumerator)block { if (!block) { RCFailAssert(@"Must be given a block to enumerate."); return; } RCAssert(_componentControllerPredicates.find(predicate) != _componentControllerPredicates.end(), @"Scope root must be initialized with predicate to enumerate."); const auto foundIter = _registeredComponentControllers.find(predicate); if (foundIter != _registeredComponentControllers.end()) { for (id<CKComponentControllerProtocol> componentController in foundIter->second) { block(componentController); } } } - (CKCocoaCollectionAdapter<id<CKComponentControllerProtocol>>)componentControllersMatchingPredicate:(CKComponentControllerPredicate)predicate { RCAssert(_componentControllerPredicates.find(predicate) != _componentControllerPredicates.end(), @"Scope root must be initialized with predicate to enumerate."); const auto componentControllersIt = _registeredComponentControllers.find(predicate); const auto componentControllers = componentControllersIt != _registeredComponentControllers.end() ? componentControllersIt->second : @[]; return CKCocoaCollectionAdapter<id<CKComponentControllerProtocol>>(componentControllers); } - (CKRootTreeNode &)rootNode { return _rootNode; } #if DEBUG - (NSString *)debugDescription { id node = _rootNode.node(); NSString *scopesTree = [[node debugDescriptionComponents] componentsJoinedByString:@"\n"]; NSString *nodesTree = [[node debugDescriptionNodes] componentsJoinedByString:@"\n"]; return [NSString stringWithFormat:@"Full Components Tree:\n%@\nScopes Components Tree:\n%@", nodesTree, scopesTree]; } #endif @end