[ReactNative] Remove bridge retaining cycles

This commit is contained in:
Tadeu Zagallo 2015-04-07 19:18:41 -07:00
Родитель 72390239be
Коммит 6854da9b86
7 изменённых файлов: 52 добавлений и 38 удалений

Просмотреть файл

@ -610,7 +610,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
selector:@selector(reload)
name:RCTReloadNotification
object:nil];
;
}
}];
}
@ -619,6 +618,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)bindKeys
{
#if TARGET_IPHONE_SIMULATOR
__weak RCTBridge *weakSelf = self;
// Workaround around the first cmd+r not working: http://openradar.appspot.com/19613391
// You can register just the cmd key and do nothing. This will trigger the bug and cmd+r
// will work like a charm!
@ -627,27 +628,33 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
action:^(UIKeyCommand *command) {
// Do nothing
}];
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"r"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
[self reload];
[weakSelf reload];
}];
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"n"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
_executorClass = Nil;
[self reload];
RCTBridge *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_executorClass = Nil;
[strongSelf reload];
}];
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
_executorClass = NSClassFromString(@"RCTWebSocketExecutor");
if (!_executorClass) {
RCTBridge *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_executorClass = NSClassFromString(@"RCTWebSocketExecutor");
if (!strongSelf->_executorClass) {
RCTLogError(@"WebSocket debugger is not available. Did you forget to include RCTWebSocketExecutor?");
}
[self reload];
[strongSelf reload];
}];
#endif
}
@ -662,7 +669,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)dealloc
{
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
[self invalidate];
}
#pragma mark - RCTInvalidating
@ -674,6 +681,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (void)invalidate
{
if (!self.isValid && _modulesByID == nil) {
return;
}
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES];
return;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
// Wait for queued methods to finish

Просмотреть файл

@ -31,7 +31,7 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
* will be set automatically by the bridge when it initializes the module.
* To implement this in your module, just add @synthesize bridge = _bridge;
*/
@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, weak) RCTBridge *bridge;
/**
* The module name exposed to JS. If omitted, this will be inferred

Просмотреть файл

@ -24,7 +24,7 @@
*/
@implementation RCTJavaScriptLoader
{
RCTBridge *_bridge;
__weak RCTBridge *_bridge;
}
/**

Просмотреть файл

@ -92,19 +92,6 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
- (void)setUp
{
if (!_registered) {
/**
* Every root view that is created must have a unique react tag.
* Numbering of these tags goes from 1, 11, 21, 31, etc
*
* NOTE: Since the bridge persists, the RootViews might be reused, so now
* the react tag is assigned every time we load new content.
*/
_contentView = [[UIView alloc] init];
_contentView.reactTag = [_bridge.uiManager allocateRootTag];
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_contentView addGestureRecognizer:_touchHandler];
[self addSubview:_contentView];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reload)
name:RCTReloadViewsNotification
@ -122,9 +109,9 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
- (void)tearDown
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (_registered) {
_registered = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_contentView removeGestureRecognizer:_touchHandler];
[_contentView removeFromSuperview];
[_touchHandler invalidate];
@ -174,6 +161,19 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
{
dispatch_async(dispatch_get_main_queue(), ^{
_registered = YES;
/**
* Every root view that is created must have a unique react tag.
* Numbering of these tags goes from 1, 11, 21, 31, etc
*
* NOTE: Since the bridge persists, the RootViews might be reused, so now
* the react tag is assigned every time we load new content.
*/
_contentView = [[UIView alloc] initWithFrame:self.bounds];
_contentView.reactTag = [_bridge.uiManager allocateRootTag];
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
[_contentView addGestureRecognizer:_touchHandler];
[self addSubview:_contentView];
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": _contentView.reactTag,
@ -197,7 +197,7 @@ NSString *const RCTReloadViewsNotification = @"RCTReloadViewsNotification";
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
_contentView.frame = self.bounds;
_contentView.frame = (CGRect){CGPointZero, frame.size};
}
- (void)reload

Просмотреть файл

@ -167,12 +167,13 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
- (void)invalidate
{
if ([NSThread currentThread] != _javaScriptThread) {
// Yes, block until done. If we're getting called right before dealloc, it's the only safe option.
[self performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:YES];
} else if (_context != NULL) {
JSGlobalContextRelease(_context);
_context = NULL;
if (self.isValid) {
if ([NSThread currentThread] != _javaScriptThread) {
[self performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:NO];
} else {
JSGlobalContextRelease(_context);
_context = NULL;
}
}
}

Просмотреть файл

@ -177,7 +177,7 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
@implementation RCTUIManager
{
dispatch_queue_t _shadowQueue;
__weak dispatch_queue_t _shadowQueue;
// Root views are only mutated on the shadow queue
NSMutableSet *_rootViewTags;
@ -319,7 +319,6 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
// Register shadow view
dispatch_async(_shadowQueue, ^{
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
shadowView.reactTag = reactTag;
shadowView.frame = frame;
@ -934,11 +933,9 @@ static void RCTMeasureLayout(RCTShadowView *view,
RCTResponseSenderBlock callback)
{
if (!view) {
RCTLogError(@"Attempting to measure view that does not exist");
return;
}
if (!ancestor) {
RCTLogError(@"Attempting to measure relative to ancestor that does not exist");
return;
}
CGRect result = [view measureLayoutRelativeToAncestor:ancestor];

Просмотреть файл

@ -28,7 +28,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
* allowing the manager (or the views that it manages) to manipulate the view
* hierarchy and send events back to the JS context.
*/
@property (nonatomic, strong) RCTBridge *bridge;
@property (nonatomic, weak) RCTBridge *bridge;
/**
* The module name exposed to React JS. If omitted, this will be inferred