mirror of
https://github.com/zhigang1992/react-native-reanimated.git
synced 2026-06-15 02:19:30 +08:00
After start/stop API landed we started evaluating graph of nodes in topological order. In order for that to work we needed to create a stack of final node to be evaluated and run it in particular order. What might happen during evaluation is that some new nodes can be marked as updated (e.g. we use set node to update a value in a different nodes subtree). In such a case we want to update views that newly marked node may have impact on. Here we also changed Android to use ArrayList instead of SparseArray for the list of updated nodes. This is to make it consistent with iOS implementation where we use a regular list too. In practice it won't happen too often that a view is added to a queue several times. Even if that happens, we still will evaluate the node at most once per frame.
147 lines
3.1 KiB
Objective-C
147 lines
3.1 KiB
Objective-C
#import "REANode.h"
|
|
#import "REANodesManager.h"
|
|
|
|
#import <React/RCTDefines.h>
|
|
|
|
@interface REAUpdateContext ()
|
|
|
|
@property (nonatomic, nonnull) NSMutableArray<REANode *> *updatedNodes;
|
|
@property (nonatomic) NSUInteger loopID;
|
|
|
|
@end
|
|
|
|
@implementation REAUpdateContext
|
|
|
|
- (instancetype)init
|
|
{
|
|
if ((self = [super init])) {
|
|
_loopID = 1;
|
|
_updatedNodes = [NSMutableArray new];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface REANode ()
|
|
|
|
@property (nonatomic) NSUInteger lastLoopID;
|
|
@property (nonatomic) id memoizedValue;
|
|
@property (nonatomic, nullable) NSMutableArray<REANode *> *childNodes;
|
|
|
|
@end
|
|
|
|
@implementation REANode
|
|
|
|
- (instancetype)initWithID:(REANodeID)nodeID config:(NSDictionary<NSString *,id> *)config
|
|
{
|
|
if ((self = [super init])) {
|
|
_nodeID = nodeID;
|
|
_lastLoopID = 0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|
|
|
- (void)dangerouslyRescheduleEvaluate
|
|
{
|
|
_lastLoopID = 0;
|
|
[self markUpdated];
|
|
}
|
|
|
|
- (void)forceUpdateMemoizedValue:(id)value
|
|
{
|
|
_memoizedValue = value;
|
|
[self markUpdated];
|
|
}
|
|
|
|
- (id)evaluate
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
- (id)value
|
|
{
|
|
if (_lastLoopID < _updateContext.loopID) {
|
|
_lastLoopID = _updateContext.loopID;
|
|
return (_memoizedValue = [self evaluate]);
|
|
}
|
|
return _memoizedValue;
|
|
}
|
|
|
|
- (void)addChild:(REANode *)child
|
|
{
|
|
if (!_childNodes) {
|
|
_childNodes = [NSMutableArray new];
|
|
}
|
|
if (child) {
|
|
[_childNodes addObject:child];
|
|
[self dangerouslyRescheduleEvaluate];
|
|
}
|
|
}
|
|
|
|
- (void)removeChild:(REANode *)child
|
|
{
|
|
if (child) {
|
|
[_childNodes removeObject:child];
|
|
}
|
|
}
|
|
|
|
- (void)markUpdated
|
|
{
|
|
[_updateContext.updatedNodes addObject:self];
|
|
[self.nodesManager postRunUpdatesAfterAnimation];
|
|
}
|
|
|
|
+ (NSMutableArray<REANode *> *)updatedNodes
|
|
{
|
|
static NSMutableArray<REANode *> *updatedNodes;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
updatedNodes = [NSMutableArray new];
|
|
});
|
|
return updatedNodes;
|
|
}
|
|
|
|
+ (void)findAndUpdateNodes:(nonnull REANode *)node
|
|
withVisitedSet:(NSMutableSet<REANode *> *)visitedNodes
|
|
withFinalNodes:(NSMutableArray<id<REAFinalNode>> *)finalNodes
|
|
{
|
|
if ([visitedNodes containsObject:node]) {
|
|
return;
|
|
} else {
|
|
[visitedNodes addObject:node];
|
|
}
|
|
for (REANode *child in node.childNodes) {
|
|
[self findAndUpdateNodes:child withVisitedSet:visitedNodes withFinalNodes:finalNodes];
|
|
}
|
|
if ([node conformsToProtocol:@protocol(REAFinalNode)]) {
|
|
[finalNodes addObject:(id<REAFinalNode>)node];
|
|
}
|
|
}
|
|
|
|
+ (void)runPropUpdates:(REAUpdateContext *)context
|
|
{
|
|
NSMutableSet<REANode *> *visitedNodes = [NSMutableSet new];
|
|
NSMutableArray<id<REAFinalNode>> *finalNodes = [NSMutableArray new];
|
|
for (NSUInteger i = 0; i < context.updatedNodes.count; i++) {
|
|
[self findAndUpdateNodes:context.updatedNodes[i]
|
|
withVisitedSet:visitedNodes
|
|
withFinalNodes:finalNodes];
|
|
if (i == context.updatedNodes.count - 1) {
|
|
while (finalNodes.count > 0) {
|
|
// NSMutableArray used for stack implementation
|
|
[[finalNodes lastObject] update];
|
|
[finalNodes removeLastObject];
|
|
}
|
|
}
|
|
}
|
|
|
|
[context.updatedNodes removeAllObjects];
|
|
context.loopID++;
|
|
}
|
|
|
|
@end
|