mirror of
https://github.com/status-im/react-native.git
synced 2025-01-28 02:04:55 +00:00
[ReactNative] Create private underlying bridge to prevent retain cycles
This commit is contained in:
parent
b532ec000f
commit
132a9170f1
@ -17,6 +17,12 @@
|
|||||||
|
|
||||||
#define TIMEOUT_SECONDS 240
|
#define TIMEOUT_SECONDS 240
|
||||||
|
|
||||||
|
@interface RCTBridge (RCTTestRunner)
|
||||||
|
|
||||||
|
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTTestRunner
|
@implementation RCTTestRunner
|
||||||
{
|
{
|
||||||
FBSnapshotTestController *_testController;
|
FBSnapshotTestController *_testController;
|
||||||
@ -66,7 +72,7 @@
|
|||||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||||
|
|
||||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
||||||
RCTTestModule *testModule = rootView.bridge.modules[testModuleName];
|
RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName];
|
||||||
testModule.controller = _testController;
|
testModule.controller = _testController;
|
||||||
testModule.testSelector = test;
|
testModule.testSelector = test;
|
||||||
testModule.view = rootView;
|
testModule.view = rootView;
|
||||||
@ -83,8 +89,6 @@
|
|||||||
error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||||
}
|
}
|
||||||
[rootView removeFromSuperview];
|
[rootView removeFromSuperview];
|
||||||
[rootView.bridge invalidate];
|
|
||||||
[rootView invalidate];
|
|
||||||
RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view);
|
RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view);
|
||||||
vc.view = nil;
|
vc.view = nil;
|
||||||
[[RCTRedBox sharedInstance] dismiss];
|
[[RCTRedBox sharedInstance] dismiss];
|
||||||
|
@ -322,23 +322,24 @@ var MessageQueueMixin = {
|
|||||||
|
|
||||||
processBatch: function(batch) {
|
processBatch: function(batch) {
|
||||||
var self = this;
|
var self = this;
|
||||||
ReactUpdates.batchedUpdates(function() {
|
return guardReturn(function () {
|
||||||
batch.forEach(function(call) {
|
ReactUpdates.batchedUpdates(function() {
|
||||||
invariant(
|
batch.forEach(function(call) {
|
||||||
call.module === 'BatchedBridge',
|
invariant(
|
||||||
'All the calls should pass through the BatchedBridge module'
|
call.module === 'BatchedBridge',
|
||||||
);
|
'All the calls should pass through the BatchedBridge module'
|
||||||
if (call.method === 'callFunctionReturnFlushedQueue') {
|
);
|
||||||
self.callFunction.apply(self, call.args);
|
if (call.method === 'callFunctionReturnFlushedQueue') {
|
||||||
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
self._callFunction.apply(self, call.args);
|
||||||
self.invokeCallback.apply(self, call.args);
|
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
||||||
} else {
|
self._invokeCallback.apply(self, call.args);
|
||||||
throw new Error(
|
} else {
|
||||||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
throw new Error(
|
||||||
}
|
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}, null, this._flushedQueueUnguarded, this);
|
||||||
return this.flushedQueue();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setLoggingEnabled: function(enabled) {
|
setLoggingEnabled: function(enabled) {
|
||||||
|
@ -80,15 +80,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
|||||||
__attribute__((used, section("__DATA,RCTImport"))) \
|
__attribute__((used, section("__DATA,RCTImport"))) \
|
||||||
static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is used to execute a new application script. It is called
|
|
||||||
* internally whenever a JS application bundle is loaded/reloaded, but should
|
|
||||||
* probably not be used at any other time.
|
|
||||||
*/
|
|
||||||
- (void)enqueueApplicationScript:(NSString *)script
|
|
||||||
url:(NSURL *)url
|
|
||||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL of the script that was loaded into the bridge.
|
* URL of the script that was loaded into the bridge.
|
||||||
*/
|
*/
|
||||||
@ -122,14 +113,4 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
|||||||
*/
|
*/
|
||||||
- (void)reload;
|
- (void)reload;
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new observer that will be called on every screen refresh.
|
|
||||||
*/
|
|
||||||
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop receiving screen refresh updates for the given observer.
|
|
||||||
*/
|
|
||||||
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#import "RCTProfile.h"
|
#import "RCTProfile.h"
|
||||||
#import "RCTRedBox.h"
|
#import "RCTRedBox.h"
|
||||||
#import "RCTRootView.h"
|
#import "RCTRootView.h"
|
||||||
|
#import "RCTSourceCode.h"
|
||||||
#import "RCTSparseArray.h"
|
#import "RCTSparseArray.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
@ -45,12 +46,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
|||||||
RCTBridgeFieldFlushDateMillis
|
RCTBridgeFieldFlushDateMillis
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporarily allow to turn on and off the call batching in case someone wants
|
|
||||||
* to profile both
|
|
||||||
*/
|
|
||||||
#define BATCHED_BRIDGE 0
|
|
||||||
|
|
||||||
#ifdef __LP64__
|
#ifdef __LP64__
|
||||||
typedef uint64_t RCTHeaderValue;
|
typedef uint64_t RCTHeaderValue;
|
||||||
typedef struct section_64 RCTHeaderSection;
|
typedef struct section_64 RCTHeaderSection;
|
||||||
@ -61,6 +56,11 @@ typedef struct section RCTHeaderSection;
|
|||||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define RCTAssertJSThread() \
|
||||||
|
RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \
|
||||||
|
[[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \
|
||||||
|
@"This method must be called on JS thread")
|
||||||
|
|
||||||
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
||||||
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
||||||
|
|
||||||
@ -122,6 +122,7 @@ static NSArray *RCTJSMethods(void)
|
|||||||
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
||||||
* functionality is disabled in release mode to improve startup performance.
|
* functionality is disabled in release mode to improve startup performance.
|
||||||
*/
|
*/
|
||||||
|
static NSDictionary *RCTModuleIDsByName;
|
||||||
static NSArray *RCTModuleNamesByID;
|
static NSArray *RCTModuleNamesByID;
|
||||||
static NSArray *RCTModuleClassesByID;
|
static NSArray *RCTModuleClassesByID;
|
||||||
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||||
@ -129,8 +130,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&onceToken, ^{
|
dispatch_once(&onceToken, ^{
|
||||||
|
|
||||||
RCTModuleNamesByID = [NSMutableArray array];
|
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
||||||
RCTModuleClassesByID = [NSMutableArray array];
|
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
||||||
|
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
Dl_info info;
|
Dl_info info;
|
||||||
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
||||||
@ -162,7 +164,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||||||
NSStringFromClass(cls));
|
NSStringFromClass(cls));
|
||||||
|
|
||||||
// Register module
|
// Register module
|
||||||
[(NSMutableArray *)RCTModuleNamesByID addObject:RCTBridgeModuleNameForClass(cls)];
|
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
||||||
|
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
||||||
|
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||||
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,20 +203,31 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||||||
return RCTModuleClassesByID;
|
return RCTModuleClassesByID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@class RCTBatchedBridge;
|
||||||
|
|
||||||
@interface RCTBridge ()
|
@interface RCTBridge ()
|
||||||
|
|
||||||
|
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
||||||
|
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
||||||
|
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
||||||
|
|
||||||
- (void)_invokeAndProcessModule:(NSString *)module
|
- (void)_invokeAndProcessModule:(NSString *)module
|
||||||
method:(NSString *)method
|
method:(NSString *)method
|
||||||
arguments:(NSArray *)args
|
arguments:(NSArray *)args
|
||||||
context:(NSNumber *)context;
|
context:(NSNumber *)context;
|
||||||
|
|
||||||
#if BATCHED_BRIDGE
|
@end
|
||||||
|
|
||||||
|
@interface RCTBatchedBridge : RCTBridge <RCTInvalidating>
|
||||||
|
|
||||||
|
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||||
|
|
||||||
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
||||||
method:(NSString *)method
|
method:(NSString *)method
|
||||||
arguments:(NSArray *)args
|
arguments:(NSArray *)args
|
||||||
context:(NSNumber *)context;
|
context:(NSNumber *)context;
|
||||||
#endif
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -238,8 +253,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||||||
dispatch_block_t _methodQueue;
|
dispatch_block_t _methodQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Class _globalExecutorClass;
|
|
||||||
|
|
||||||
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
||||||
{
|
{
|
||||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||||
@ -702,6 +715,7 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||||||
@"methods": [[NSMutableDictionary alloc] init]
|
@"methods": [[NSMutableDictionary alloc] init]
|
||||||
};
|
};
|
||||||
localModules[moduleName] = module;
|
localModules[moduleName] = module;
|
||||||
|
[RCTLocalModuleNames addObject:moduleName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add method if it doesn't already exist
|
// Add method if it doesn't already exist
|
||||||
@ -712,72 +726,18 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||||||
@"methodID": @(methods.count),
|
@"methodID": @(methods.count),
|
||||||
@"type": @"local"
|
@"type": @"local"
|
||||||
};
|
};
|
||||||
|
[RCTLocalMethodNames addObject:methodName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add module and method lookup
|
// Add module and method lookup
|
||||||
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
||||||
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||||
[RCTLocalModuleNames addObject:moduleName];
|
|
||||||
[RCTLocalMethodNames addObject:methodName];
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return localModules;
|
return localModules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@interface RCTDisplayLink : NSObject <RCTInvalidating>
|
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector NS_DESIGNATED_INITIALIZER;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface RCTBridge (RCTDisplayLink)
|
|
||||||
|
|
||||||
- (void)_update:(CADisplayLink *)displayLink;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation RCTDisplayLink
|
|
||||||
{
|
|
||||||
__weak RCTBridge *_bridge;
|
|
||||||
CADisplayLink *_displayLink;
|
|
||||||
SEL _selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector
|
|
||||||
{
|
|
||||||
if ((self = [super init])) {
|
|
||||||
_bridge = bridge;
|
|
||||||
_selector = selector;
|
|
||||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)];
|
|
||||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isValid
|
|
||||||
{
|
|
||||||
return _displayLink != nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidate
|
|
||||||
{
|
|
||||||
if (self.isValid) {
|
|
||||||
[_displayLink invalidate];
|
|
||||||
_displayLink = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_update:(CADisplayLink *)displayLink
|
|
||||||
{
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
||||||
[_bridge performSelector:_selector withObject:displayLink];
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface RCTFrameUpdate (Private)
|
@interface RCTFrameUpdate (Private)
|
||||||
|
|
||||||
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
|
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
|
||||||
@ -798,22 +758,6 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTBridge
|
@implementation RCTBridge
|
||||||
{
|
|
||||||
RCTSparseArray *_modulesByID;
|
|
||||||
RCTSparseArray *_queuesByID;
|
|
||||||
dispatch_queue_t _methodQueue;
|
|
||||||
NSDictionary *_modulesByName;
|
|
||||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
|
||||||
Class _executorClass;
|
|
||||||
NSURL *_bundleURL;
|
|
||||||
RCTBridgeModuleProviderBlock _moduleProvider;
|
|
||||||
RCTDisplayLink *_displayLink;
|
|
||||||
RCTDisplayLink *_vsyncDisplayLink;
|
|
||||||
NSMutableSet *_frameUpdateObservers;
|
|
||||||
NSMutableArray *_scheduledCalls;
|
|
||||||
RCTSparseArray *_scheduledCallbacks;
|
|
||||||
BOOL _loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
|
|
||||||
@ -821,36 +765,226 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||||
launchOptions:(NSDictionary *)launchOptions
|
launchOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
/**
|
||||||
|
* Pre register modules
|
||||||
|
*/
|
||||||
|
RCTLocalModulesConfig();
|
||||||
|
|
||||||
_bundleURL = bundleURL;
|
_bundleURL = bundleURL;
|
||||||
_moduleProvider = block;
|
_moduleProvider = block;
|
||||||
_launchOptions = [launchOptions copy];
|
_launchOptions = [launchOptions copy];
|
||||||
|
|
||||||
[self setUp];
|
|
||||||
[self bindKeys];
|
[self bindKeys];
|
||||||
|
[self setUp];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This runs only on the main thread, but crashes the subclass
|
||||||
|
* RCTAssertMainThread();
|
||||||
|
*/
|
||||||
|
[self invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)bindKeys
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(reload)
|
||||||
|
name:RCTReloadNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
|
|
||||||
|
__weak RCTBridge *weakSelf = self;
|
||||||
|
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
||||||
|
|
||||||
|
// reload in current mode
|
||||||
|
[commands registerKeyCommandWithInput:@"r"
|
||||||
|
modifierFlags:UIKeyModifierCommand
|
||||||
|
action:^(UIKeyCommand *command) {
|
||||||
|
[weakSelf reload];
|
||||||
|
}];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reload
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* AnyThread
|
||||||
|
*/
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self invalidate];
|
||||||
|
[self setUp];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
- (void)setUp
|
- (void)setUp
|
||||||
{
|
{
|
||||||
Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class];
|
RCTAssertMainThread();
|
||||||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
|
||||||
_latestJSExecutor = _javaScriptExecutor;
|
|
||||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
|
||||||
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
|
|
||||||
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
|
||||||
_scheduledCalls = [[NSMutableArray alloc] init];
|
|
||||||
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
|
||||||
|
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
_batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
|
||||||
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)];
|
}
|
||||||
}];
|
|
||||||
_vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)];
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return _batchedBridge.isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
[_batchedBridge invalidate];
|
||||||
|
_batchedBridge = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
||||||
|
{
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (!_latestJSExecutor.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_latestJSExecutor executeJSCall:@"RCTLog"
|
||||||
|
method:@"logIfNoNativeHook"
|
||||||
|
arguments:@[level, message]
|
||||||
|
context:RCTGetExecutorID(_latestJSExecutor)
|
||||||
|
callback:^(id json, NSError *error) {}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)modules
|
||||||
|
{
|
||||||
|
return _batchedBridge.modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RCT_BRIDGE_WARN(...) \
|
||||||
|
- (void)__VA_ARGS__ \
|
||||||
|
{ \
|
||||||
|
RCTLogMustFix(@"Called method \"%@\" on top level bridge. This method should \
|
||||||
|
only be called from bridge instance in a bridge module", @(__func__)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_BRIDGE_WARN(enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args)
|
||||||
|
RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTBatchedBridge
|
||||||
|
{
|
||||||
|
BOOL _loading;
|
||||||
|
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||||
|
RCTSparseArray *_modulesByID;
|
||||||
|
RCTSparseArray *_queuesByID;
|
||||||
|
dispatch_queue_t _methodQueue;
|
||||||
|
NSDictionary *_modulesByName;
|
||||||
|
CADisplayLink *_mainDisplayLink;
|
||||||
|
CADisplayLink *_jsDisplayLink;
|
||||||
|
NSMutableSet *_frameUpdateObservers;
|
||||||
|
NSMutableArray *_scheduledCalls;
|
||||||
|
RCTSparseArray *_scheduledCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@synthesize valid = _valid;
|
||||||
|
|
||||||
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
_parentBridge = bridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Initial State
|
||||||
|
*/
|
||||||
|
_valid = YES;
|
||||||
|
_loading = YES;
|
||||||
|
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
||||||
|
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||||
|
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||||
|
_queuesByID = [[RCTSparseArray alloc] init];
|
||||||
|
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize executor to allow enqueueing calls
|
||||||
|
*/
|
||||||
|
Class executorClass = self.executorClass ?: [RCTContextExecutor class];
|
||||||
|
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
||||||
|
_latestJSExecutor = _javaScriptExecutor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup event dispatcher before initializing modules to allow init calls
|
||||||
|
*/
|
||||||
|
self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and register bridge modules *before* adding the display link
|
||||||
|
* so we don't have threading issues
|
||||||
|
*/
|
||||||
|
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
[self registerModules];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the application script
|
||||||
|
*/
|
||||||
|
[self initJS];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)launchOptions
|
||||||
|
{
|
||||||
|
return _parentBridge.launchOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override to ensure that we won't create another nested bridge
|
||||||
|
*/
|
||||||
|
- (void)setUp {}
|
||||||
|
|
||||||
|
- (void)reload
|
||||||
|
{
|
||||||
|
[_parentBridge reload];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (Class)executorClass
|
||||||
|
{
|
||||||
|
return _parentBridge.executorClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setExecutorClass:(Class)executorClass
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
_parentBridge.executorClass = executorClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isLoading
|
||||||
|
{
|
||||||
|
return _loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return _valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerModules
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
// Register passed-in module instances
|
// Register passed-in module instances
|
||||||
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
||||||
for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) {
|
for (id<RCTBridgeModule> module in _parentBridge.moduleProvider ? _parentBridge.moduleProvider() : nil) {
|
||||||
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,7 +1031,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get method queues
|
// Get method queues
|
||||||
_queuesByID = [[RCTSparseArray alloc] init];
|
|
||||||
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
||||||
if ([module respondsToSelector:@selector(methodQueue)]) {
|
if ([module respondsToSelector:@selector(methodQueue)]) {
|
||||||
dispatch_queue_t queue = [module methodQueue];
|
dispatch_queue_t queue = [module methodQueue];
|
||||||
@ -907,7 +1040,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
_queuesByID[moduleID] = [NSNull null];
|
_queuesByID[moduleID] = [NSNull null];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||||
|
[_frameUpdateObservers addObject:module];
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initJS
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
// Inject module data into JS context
|
// Inject module data into JS context
|
||||||
NSString *configJSON = RCTJSONStringify(@{
|
NSString *configJSON = RCTJSONStringify(@{
|
||||||
@ -920,7 +1062,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
dispatch_semaphore_signal(semaphore);
|
dispatch_semaphore_signal(semaphore);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
_loading = YES;
|
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
|
||||||
|
|
||||||
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||||
if (_javaScriptExecutor == nil) {
|
if (_javaScriptExecutor == nil) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -929,11 +1073,17 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
*/
|
*/
|
||||||
_loading = NO;
|
_loading = NO;
|
||||||
|
|
||||||
} else if (_bundleURL) { // Allow testing without a script
|
} else if (bundleURL) { // Allow testing without a script
|
||||||
|
|
||||||
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
||||||
[loader loadBundleAtURL:_bundleURL onComplete:^(NSError *error) {
|
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
||||||
_loading = NO;
|
_loading = NO;
|
||||||
|
if (!self.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||||
|
sourceCodeModule.scriptURL = bundleURL;
|
||||||
|
sourceCodeModule.scriptText = script;
|
||||||
if (error != nil) {
|
if (error != nil) {
|
||||||
|
|
||||||
NSArray *stack = [[error userInfo] objectForKey:@"stack"];
|
NSArray *stack = [[error userInfo] objectForKey:@"stack"];
|
||||||
@ -946,37 +1096,25 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
if (!loadError) {
|
||||||
object:self];
|
/**
|
||||||
|
* Register the display link to start sending js calls after everything
|
||||||
|
* is setup
|
||||||
|
*/
|
||||||
|
[_jsDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:@{ @"bridge": self }];
|
||||||
|
}
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
||||||
selector:@selector(reload)
|
|
||||||
name:RCTReloadNotification
|
|
||||||
object:nil];
|
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)bindKeys
|
|
||||||
{
|
|
||||||
|
|
||||||
#if TARGET_IPHONE_SIMULATOR
|
|
||||||
|
|
||||||
__weak RCTBridge *weakSelf = self;
|
|
||||||
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
|
||||||
|
|
||||||
// reload in current mode
|
|
||||||
[commands registerKeyCommandWithInput:@"r"
|
|
||||||
modifierFlags:UIKeyModifierCommand
|
|
||||||
action:^(UIKeyCommand *command) {
|
|
||||||
[weakSelf reload];
|
|
||||||
}];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)modules
|
- (NSDictionary *)modules
|
||||||
{
|
{
|
||||||
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
||||||
@ -985,53 +1123,48 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
return _modulesByName;
|
return _modulesByName;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[self invalidate];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - RCTInvalidating
|
#pragma mark - RCTInvalidating
|
||||||
|
|
||||||
- (BOOL)isValid
|
|
||||||
{
|
|
||||||
return _javaScriptExecutor != nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
if (!self.isValid && _modulesByID == nil) {
|
if (!self.isValid) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![NSThread isMainThread]) {
|
RCTAssertMainThread();
|
||||||
[self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
_valid = NO;
|
||||||
|
|
||||||
// Release executor
|
|
||||||
if (_latestJSExecutor == _javaScriptExecutor) {
|
if (_latestJSExecutor == _javaScriptExecutor) {
|
||||||
_latestJSExecutor = nil;
|
_latestJSExecutor = nil;
|
||||||
}
|
}
|
||||||
[_javaScriptExecutor invalidate];
|
|
||||||
_javaScriptExecutor = nil;
|
|
||||||
|
|
||||||
[_displayLink invalidate];
|
/**
|
||||||
[_vsyncDisplayLink invalidate];
|
* Main Thread deallocations
|
||||||
_frameUpdateObservers = nil;
|
*/
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[_mainDisplayLink invalidate];
|
||||||
|
|
||||||
// Invalidate modules
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
for (id target in _modulesByID.allObjects) {
|
/**
|
||||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
* JS Thread deallocations
|
||||||
[(id<RCTInvalidating>)target invalidate];
|
*/
|
||||||
|
[_javaScriptExecutor invalidate];
|
||||||
|
[_jsDisplayLink invalidate];
|
||||||
|
|
||||||
|
// Invalidate modules
|
||||||
|
for (id target in _modulesByID.allObjects) {
|
||||||
|
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||||
|
[(id<RCTInvalidating>)target invalidate];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||||
_modulesByID = nil;
|
_javaScriptExecutor = nil;
|
||||||
_queuesByID = nil;
|
_frameUpdateObservers = nil;
|
||||||
_modulesByName = nil;
|
_modulesByID = nil;
|
||||||
|
_queuesByID = nil;
|
||||||
|
_modulesByName = nil;
|
||||||
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1066,6 +1199,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
*/
|
*/
|
||||||
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
||||||
{
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
NSString *moduleDotMethod = @"RCTJSTimers.callTimers";
|
NSString *moduleDotMethod = @"RCTJSTimers.callTimers";
|
||||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||||
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
||||||
@ -1074,35 +1209,29 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||||
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
||||||
|
|
||||||
if (!_loading) {
|
dispatch_block_t block = ^{
|
||||||
#if BATCHED_BRIDGE
|
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||||
dispatch_block_t block = ^{
|
method:@"callFunctionReturnFlushedQueue"
|
||||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||||
method:@"callFunctionReturnFlushedQueue"
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
};
|
||||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
|
||||||
};
|
|
||||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
|
||||||
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
|
||||||
} else {
|
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||||
|
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
} else {
|
||||||
method:@"callFunctionReturnFlushedQueue"
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
|
||||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||||
{
|
{
|
||||||
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
||||||
|
|
||||||
RCTProfileBeginEvent();
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
|
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
|
||||||
if (scriptLoadError) {
|
if (scriptLoadError) {
|
||||||
onComplete(scriptLoadError);
|
onComplete(scriptLoadError);
|
||||||
@ -1132,7 +1261,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
|
|
||||||
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
|
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
|
||||||
{
|
{
|
||||||
id queue = _queuesByID[moduleID];
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
id queue = nil;
|
||||||
|
if (moduleID) {
|
||||||
|
queue = _queuesByID[moduleID];
|
||||||
|
}
|
||||||
|
|
||||||
if (queue == [NSNull null]) {
|
if (queue == [NSNull null]) {
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||||
} else {
|
} else {
|
||||||
@ -1146,13 +1281,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
*/
|
*/
|
||||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||||
{
|
{
|
||||||
#if BATCHED_BRIDGE
|
/**
|
||||||
|
* AnyThread
|
||||||
|
*/
|
||||||
|
|
||||||
__weak RCTBridge *weakSelf = self;
|
__weak RCTBatchedBridge *weakSelf = self;
|
||||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||||
RCTProfileBeginEvent();
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
RCTBridge *strongSelf = weakSelf;
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1190,7 +1327,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
*/
|
*/
|
||||||
if (
|
if (
|
||||||
[args[2][0] isEqual:callArgs[2][0]] &&
|
[args[2][0] isEqual:callArgs[2][0]] &&
|
||||||
([moduleName isEqualToString:@"RCTEventEmitter"] ? [args[2][1] isEqual:callArgs[2][1]] : YES)
|
(![moduleName isEqualToString:@"RCTEventEmitter"] || [args[2][1] isEqual:callArgs[2][1]])
|
||||||
) {
|
) {
|
||||||
[strongSelf->_scheduledCalls removeObject:call];
|
[strongSelf->_scheduledCalls removeObject:call];
|
||||||
}
|
}
|
||||||
@ -1218,10 +1355,14 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
|
|
||||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||||
{
|
{
|
||||||
#endif
|
RCTAssertJSThread();
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
||||||
|
|
||||||
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
||||||
|
if (!self.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||||
[self _handleBuffer:json context:context];
|
[self _handleBuffer:json context:context];
|
||||||
};
|
};
|
||||||
@ -1237,6 +1378,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
|
|
||||||
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
||||||
{
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
if (buffer == nil || buffer == (id)kCFNull) {
|
if (buffer == nil || buffer == (id)kCFNull) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1307,6 +1450,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
params:(NSArray *)params
|
params:(NSArray *)params
|
||||||
context:(NSNumber *)context
|
context:(NSNumber *)context
|
||||||
{
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
|
if (!self.isValid) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
||||||
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||||
@ -1330,10 +1478,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak RCTBridge *weakSelf = self;
|
__weak RCTBatchedBridge *weakSelf = self;
|
||||||
[self dispatchBlock:^{
|
[self dispatchBlock:^{
|
||||||
RCTProfileBeginEvent();
|
RCTProfileBeginEvent();
|
||||||
__strong RCTBridge *strongSelf = weakSelf;
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
|
|
||||||
if (!strongSelf.isValid) {
|
if (!strongSelf.isValid) {
|
||||||
// strongSelf has been invalidated since the dispatch_async call and this
|
// strongSelf has been invalidated since the dispatch_async call and this
|
||||||
@ -1367,18 +1515,21 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
|
|
||||||
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
||||||
{
|
{
|
||||||
|
RCTAssertJSThread();
|
||||||
|
|
||||||
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
||||||
|
|
||||||
RCTProfileBeginEvent();
|
RCTProfileBeginEvent();
|
||||||
|
|
||||||
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
||||||
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
|
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
|
||||||
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
|
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
|
||||||
[observer didUpdateFrame:frameUpdate];
|
[self dispatchBlock:^{
|
||||||
|
[observer didUpdateFrame:frameUpdate];
|
||||||
|
} forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BATCHED_BRIDGE
|
|
||||||
|
|
||||||
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
|
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
|
||||||
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
||||||
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
||||||
@ -1393,67 +1544,41 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||||
{
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
|
|
||||||
{
|
|
||||||
[_frameUpdateObservers addObject:observer];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
|
|
||||||
{
|
|
||||||
[_frameUpdateObservers removeObject:observer];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reload
|
|
||||||
{
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
if (!_loading) {
|
|
||||||
// If the bridge has not loaded yet, the context will be already invalid at
|
|
||||||
// the time the javascript gets executed.
|
|
||||||
// It will crash the javascript, and even the next `load` won't render.
|
|
||||||
[self invalidate];
|
|
||||||
[self setUp];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
|
||||||
{
|
|
||||||
if (![_latestJSExecutor isValid]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: the js executor could get invalidated while we're trying to call
|
|
||||||
// this...need to watch out for that.
|
|
||||||
[_latestJSExecutor executeJSCall:@"RCTLog"
|
|
||||||
method:@"logIfNoNativeHook"
|
|
||||||
arguments:@[level, message]
|
|
||||||
context:RCTGetExecutorID(_latestJSExecutor)
|
|
||||||
callback:^(id json, NSError *error) {}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startProfiling
|
- (void)startProfiling
|
||||||
{
|
{
|
||||||
if (![_bundleURL.scheme isEqualToString:@"http"]) {
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
|
||||||
RCTLogError(@"To run the profiler you must be running from the dev server");
|
RCTLogError(@"To run the profiler you must be running from the dev server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[_mainDisplayLink invalidate];
|
||||||
|
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||||
|
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||||
|
|
||||||
RCTProfileInit();
|
RCTProfileInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stopProfiling
|
- (void)stopProfiling
|
||||||
{
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
[_mainDisplayLink invalidate];
|
||||||
|
|
||||||
NSString *log = RCTProfileEnd();
|
NSString *log = RCTProfileEnd();
|
||||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _bundleURL.scheme, _bundleURL.host, _bundleURL.port];
|
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||||
|
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
|
||||||
NSURL *URL = [NSURL URLWithString:URLString];
|
NSURL *URL = [NSURL URLWithString:URLString];
|
||||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
||||||
URLRequest.HTTPMethod = @"POST";
|
URLRequest.HTTPMethod = @"POST";
|
||||||
|
@ -123,10 +123,12 @@ RCT_EXPORT_MODULE()
|
|||||||
{
|
{
|
||||||
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
|
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
|
||||||
|
|
||||||
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||||
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||||
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||||
|
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)jsLoaded
|
- (void)jsLoaded
|
||||||
@ -147,10 +149,12 @@ RCT_EXPORT_MODULE()
|
|||||||
_liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
_liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hit these setters again after bridge has finished loading
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
self.profilingEnabled = _profilingEnabled;
|
// Hit these setters again after bridge has finished loading
|
||||||
self.liveReloadEnabled = _liveReloadEnabled;
|
self.profilingEnabled = _profilingEnabled;
|
||||||
self.executorClass = _executorClass;
|
self.liveReloadEnabled = _liveReloadEnabled;
|
||||||
|
self.executorClass = _executorClass;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
|
@ -22,6 +22,6 @@
|
|||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete
|
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete
|
||||||
{
|
{
|
||||||
// Sanitize the script URL
|
// Sanitize the script URL
|
||||||
scriptURL = [RCTConvert NSURL:scriptURL.absoluteString];
|
scriptURL = [RCTConvert NSURL:scriptURL.absoluteString];
|
||||||
@ -37,7 +37,7 @@
|
|||||||
NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{
|
NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{
|
||||||
NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided"
|
NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided"
|
||||||
}];
|
}];
|
||||||
onComplete(error);
|
onComplete(error, nil);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@
|
|||||||
code:error.code
|
code:error.code
|
||||||
userInfo:userInfo];
|
userInfo:userInfo];
|
||||||
}
|
}
|
||||||
onComplete(error);
|
onComplete(error, nil);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,18 +96,10 @@
|
|||||||
code:[(NSHTTPURLResponse *)response statusCode]
|
code:[(NSHTTPURLResponse *)response statusCode]
|
||||||
userInfo:userInfo];
|
userInfo:userInfo];
|
||||||
|
|
||||||
onComplete(error);
|
onComplete(error, nil);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
onComplete(nil, rawText);
|
||||||
sourceCodeModule.scriptURL = scriptURL;
|
|
||||||
sourceCodeModule.scriptText = rawText;
|
|
||||||
|
|
||||||
[_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
onComplete(scriptError);
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[task resume];
|
[task resume];
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
|
|
||||||
@interface RCTRootView : UIView <RCTInvalidating>
|
@interface RCTRootView : UIView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* - Designated initializer -
|
* - Designated initializer -
|
||||||
|
@ -20,25 +20,37 @@
|
|||||||
#import "RCTTouchHandler.h"
|
#import "RCTTouchHandler.h"
|
||||||
#import "RCTUIManager.h"
|
#import "RCTUIManager.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTView.h"
|
||||||
#import "RCTWebViewExecutor.h"
|
#import "RCTWebViewExecutor.h"
|
||||||
#import "UIView+React.h"
|
#import "UIView+React.h"
|
||||||
|
|
||||||
|
@interface RCTBridge (RCTRootView)
|
||||||
|
|
||||||
|
@property (nonatomic, weak, readonly) RCTBridge *batchedBridge;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface RCTUIManager (RCTRootView)
|
@interface RCTUIManager (RCTRootView)
|
||||||
|
|
||||||
- (NSNumber *)allocateRootTag;
|
- (NSNumber *)allocateRootTag;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface RCTRootContentView : RCTView <RCTInvalidating>
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTRootView
|
@implementation RCTRootView
|
||||||
{
|
{
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
RCTTouchHandler *_touchHandler;
|
|
||||||
NSString *_moduleName;
|
NSString *_moduleName;
|
||||||
NSDictionary *_launchOptions;
|
NSDictionary *_launchOptions;
|
||||||
UIView *_contentView;
|
RCTRootContentView *_contentView;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
moduleName:(NSString *)moduleName
|
moduleName:(NSString *)moduleName
|
||||||
{
|
{
|
||||||
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
|
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
|
||||||
@ -52,11 +64,11 @@
|
|||||||
_moduleName = moduleName;
|
_moduleName = moduleName;
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
selector:@selector(bundleFinishedLoading)
|
selector:@selector(javaScriptDidLoad:)
|
||||||
name:RCTJavaScriptDidLoadNotification
|
name:RCTJavaScriptDidLoadNotification
|
||||||
object:_bridge];
|
object:_bridge];
|
||||||
if (!_bridge.loading) {
|
if (!_bridge.batchedBridge.isLoading) {
|
||||||
[self bundleFinishedLoading];
|
[self bundleFinishedLoading:_bridge.batchedBridge];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@ -73,25 +85,6 @@
|
|||||||
return [self initWithBridge:bridge moduleName:moduleName];
|
return [self initWithBridge:bridge moduleName:moduleName];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isValid
|
|
||||||
{
|
|
||||||
return _contentView.userInteractionEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)invalidate
|
|
||||||
{
|
|
||||||
_contentView.userInteractionEnabled = NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
if (_contentView) {
|
|
||||||
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
|
||||||
args:@[_contentView.reactTag]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UIViewController *)backingViewController
|
- (UIViewController *)backingViewController
|
||||||
{
|
{
|
||||||
return _backingViewController ?: [super backingViewController];
|
return _backingViewController ?: [super backingViewController];
|
||||||
@ -105,9 +98,19 @@
|
|||||||
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
||||||
RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||||
|
|
||||||
- (void)bundleFinishedLoading
|
|
||||||
|
- (void)javaScriptDidLoad:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
RCTBridge *bridge = notification.userInfo[@"bridge"];
|
||||||
|
[self bundleFinishedLoading:bridge];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)bundleFinishedLoading:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (!bridge.isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Every root view that is created must have a unique React tag.
|
* Every root view that is created must have a unique React tag.
|
||||||
@ -117,19 +120,16 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||||||
* the React tag is assigned every time we load new content.
|
* the React tag is assigned every time we load new content.
|
||||||
*/
|
*/
|
||||||
[_contentView removeFromSuperview];
|
[_contentView removeFromSuperview];
|
||||||
_contentView = [[UIView alloc] initWithFrame:self.bounds];
|
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
|
||||||
_contentView.reactTag = [_bridge.uiManager allocateRootTag];
|
bridge:bridge];
|
||||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
|
||||||
[_contentView addGestureRecognizer:_touchHandler];
|
|
||||||
[self addSubview:_contentView];
|
[self addSubview:_contentView];
|
||||||
|
|
||||||
NSString *moduleName = _moduleName ?: @"";
|
NSString *moduleName = _moduleName ?: @"";
|
||||||
NSDictionary *appParameters = @{
|
NSDictionary *appParameters = @{
|
||||||
@"rootTag": _contentView.reactTag,
|
@"rootTag": _contentView.reactTag,
|
||||||
@"initialProps": self.initialProperties ?: @{},
|
@"initialProps": _initialProperties ?: @{},
|
||||||
};
|
};
|
||||||
[_bridge.uiManager registerRootView:_contentView];
|
[bridge enqueueJSCall:@"AppRegistry.runApplication"
|
||||||
[_bridge enqueueJSCall:@"AppRegistry.runApplication"
|
|
||||||
args:@[moduleName, appParameters]];
|
args:@[moduleName, appParameters]];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -139,7 +139,6 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
if (_contentView) {
|
if (_contentView) {
|
||||||
_contentView.frame = self.bounds;
|
_contentView.frame = self.bounds;
|
||||||
[_bridge.uiManager setFrame:self.bounds forRootView:_contentView];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,6 +147,12 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||||||
return _contentView.reactTag;
|
return _contentView.reactTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[_contentView removeFromSuperview];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RCTUIManager (RCTRootView)
|
@implementation RCTUIManager (RCTRootView)
|
||||||
@ -160,3 +165,60 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation RCTRootContentView
|
||||||
|
{
|
||||||
|
__weak RCTBridge *_bridge;
|
||||||
|
RCTTouchHandler *_touchHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
bridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_bridge = bridge;
|
||||||
|
[self setUp];
|
||||||
|
self.frame = frame;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
[super setFrame:frame];
|
||||||
|
if (self.reactTag && _bridge.isValid) {
|
||||||
|
[_bridge.uiManager setFrame:self.bounds forRootView:self];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setUp
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
self.reactTag = [_bridge.uiManager allocateRootTag];
|
||||||
|
[self addGestureRecognizer:[[RCTTouchHandler alloc] initWithBridge:_bridge]];
|
||||||
|
[_bridge.uiManager registerRootView:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isValid
|
||||||
|
{
|
||||||
|
return self.userInteractionEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)invalidate
|
||||||
|
{
|
||||||
|
self.userInteractionEnabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
||||||
|
args:@[self.reactTag]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
@synthesize paused = _paused;
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
@ -78,7 +79,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
_paused = YES;
|
||||||
_timers = [[RCTSparseArray alloc] init];
|
_timers = [[RCTSparseArray alloc] init];
|
||||||
|
|
||||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||||
@ -126,7 +127,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||||||
|
|
||||||
- (void)stopTimers
|
- (void)stopTimers
|
||||||
{
|
{
|
||||||
[_bridge removeFrameUpdateObserver:self];
|
_paused = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startTimers
|
- (void)startTimers
|
||||||
@ -135,7 +136,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[_bridge addFrameUpdateObserver:self];
|
_paused = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||||
|
@ -267,11 +267,6 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
|
||||||
{
|
|
||||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)isValid
|
- (BOOL)isValid
|
||||||
{
|
{
|
||||||
return _viewRegistry != nil;
|
return _viewRegistry != nil;
|
||||||
@ -279,20 +274,24 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
{
|
{
|
||||||
RCTAssertMainThread();
|
/**
|
||||||
|
* Called on the JS Thread since all modules are invalidated on the JS thread
|
||||||
|
*/
|
||||||
|
|
||||||
for (NSNumber *rootViewTag in _rootViewTags) {
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
for (NSNumber *rootViewTag in _rootViewTags) {
|
||||||
}
|
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
||||||
|
}
|
||||||
|
|
||||||
_rootViewTags = nil;
|
_rootViewTags = nil;
|
||||||
_shadowViewRegistry = nil;
|
_shadowViewRegistry = nil;
|
||||||
_viewRegistry = nil;
|
_viewRegistry = nil;
|
||||||
_bridge = nil;
|
_bridge = nil;
|
||||||
|
|
||||||
[_pendingUIBlocksLock lock];
|
[_pendingUIBlocksLock lock];
|
||||||
_pendingUIBlocks = nil;
|
_pendingUIBlocks = nil;
|
||||||
[_pendingUIBlocksLock unlock];
|
[_pendingUIBlocksLock unlock];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setBridge:(RCTBridge *)bridge
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
|
@ -264,9 +264,13 @@ NSInteger kNeverProgressed = -10000;
|
|||||||
NSInteger _numberOfViewControllerMovesToIgnore;
|
NSInteger _numberOfViewControllerMovesToIgnore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@synthesize paused = _paused;
|
||||||
|
|
||||||
- (id)initWithBridge:(RCTBridge *)bridge
|
- (id)initWithBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
if ((self = [super initWithFrame:CGRectZero])) {
|
if ((self = [super initWithFrame:CGRectZero])) {
|
||||||
|
_paused = YES;
|
||||||
|
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
_mostRecentProgress = kNeverProgressed;
|
_mostRecentProgress = kNeverProgressed;
|
||||||
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
|
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
@ -341,14 +345,14 @@ NSInteger kNeverProgressed = -10000;
|
|||||||
_dummyView.frame = (CGRect){{destination}};
|
_dummyView.frame = (CGRect){{destination}};
|
||||||
_currentlyTransitioningFrom = indexOfFrom;
|
_currentlyTransitioningFrom = indexOfFrom;
|
||||||
_currentlyTransitioningTo = indexOfTo;
|
_currentlyTransitioningTo = indexOfTo;
|
||||||
[_bridge addFrameUpdateObserver:self];
|
_paused = NO;
|
||||||
}
|
}
|
||||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||||
[weakSelf freeLock];
|
[weakSelf freeLock];
|
||||||
_currentlyTransitioningFrom = 0;
|
_currentlyTransitioningFrom = 0;
|
||||||
_currentlyTransitioningTo = 0;
|
_currentlyTransitioningTo = 0;
|
||||||
_dummyView.frame = CGRectZero;
|
_dummyView.frame = CGRectZero;
|
||||||
[_bridge removeFrameUpdateObserver:self];
|
_paused = YES;
|
||||||
// Reset the parallel position tracker
|
// Reset the parallel position tracker
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user