react-native/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m
Janic Duplessis c47759a9ae Fix potential retain cycles in Animated iOS
Summary:
Fixes potential retain cycles detected by an internal fb tool.

```
First:

__NSDictionaryM
-> RCTPropsAnimatedNode
-> _parentNodes -> __NSDictionaryM
-> RCTStyleAnimatedNode
-> _childNodes -> __NSDictionaryM

Second:

RCTScrollView
-> _eventDispatcher -> RCTEventDispatcher
-> _observers -> __NSArrayM
-> RCTNativeAnimatedModule
-> _nodesManager -> RCTNativeAnimatedNodesManager
-> _uiManager -> RCTUIManager
-> _viewRegistry -> __NSDictionaryM
-> RCTScrollView
```

First fix:
Use weak map for parent and child nodes, strong refs are managed by RCTNativeAnimatedNodesManager

Second fix:
Make RCTEventDispatcher observers a weak array and make sure we don't keep strong refs to UIManager in RCTNativeAnimatedNodesManager and RCTPropsAnimatedNode.

Tested that native animations still work in UIExplorer

[IOS] [BUGFIX] [NativeAnimated] - Fix potential retain cycles in Animated iOS
Closes https://github.com/facebook/react-native/pull/16506

Differential Revision: D6126400

Pulled By: shergin

fbshipit-source-id: 1ac5083f8ab79a806305edc23ae4796ed428f78b
2017-10-23 13:20:59 -07:00

60 lines
1.7 KiB
Objective-C

/**
* Copyright (c) 2015-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 "RCTTransformAnimatedNode.h"
#import "RCTValueAnimatedNode.h"
@implementation RCTTransformAnimatedNode
{
NSMutableDictionary<NSString *, NSObject *> *_propsDictionary;
}
- (instancetype)initWithTag:(NSNumber *)tag
config:(NSDictionary<NSString *, id> *)config;
{
if ((self = [super initWithTag:tag config:config])) {
_propsDictionary = [NSMutableDictionary new];
}
return self;
}
- (NSDictionary *)propsDictionary
{
return _propsDictionary;
}
- (void)performUpdate
{
[super performUpdate];
NSArray<NSDictionary *> *transformConfigs = self.config[@"transforms"];
NSMutableArray<NSDictionary *> *transform = [NSMutableArray arrayWithCapacity:transformConfigs.count];
for (NSDictionary *transformConfig in transformConfigs) {
NSString *type = transformConfig[@"type"];
NSString *property = transformConfig[@"property"];
NSNumber *value;
if ([type isEqualToString: @"animated"]) {
NSNumber *nodeTag = transformConfig[@"nodeTag"];
RCTAnimatedNode *node = [self.parentNodes objectForKey:nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
continue;
}
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
value = @(parentNode.value);
} else {
value = transformConfig[@"value"];
}
[transform addObject:@{property: value}];
}
_propsDictionary[@"transform"] = transform;
}
@end