mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 03:26:07 +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
|
||||
|
||||
@interface RCTBridge (RCTTestRunner)
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTestRunner
|
||||
{
|
||||
FBSnapshotTestController *_testController;
|
||||
@ -66,7 +72,7 @@
|
||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||
|
||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
||||
RCTTestModule *testModule = rootView.bridge.modules[testModuleName];
|
||||
RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName];
|
||||
testModule.controller = _testController;
|
||||
testModule.testSelector = test;
|
||||
testModule.view = rootView;
|
||||
@ -83,8 +89,6 @@
|
||||
error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
}
|
||||
[rootView removeFromSuperview];
|
||||
[rootView.bridge invalidate];
|
||||
[rootView invalidate];
|
||||
RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view);
|
||||
vc.view = nil;
|
||||
[[RCTRedBox sharedInstance] dismiss];
|
||||
|
@ -322,23 +322,24 @@ var MessageQueueMixin = {
|
||||
|
||||
processBatch: function(batch) {
|
||||
var self = this;
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
batch.forEach(function(call) {
|
||||
invariant(
|
||||
call.module === 'BatchedBridge',
|
||||
'All the calls should pass through the BatchedBridge module'
|
||||
);
|
||||
if (call.method === 'callFunctionReturnFlushedQueue') {
|
||||
self.callFunction.apply(self, call.args);
|
||||
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
||||
self.invokeCallback.apply(self, call.args);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||
}
|
||||
return guardReturn(function () {
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
batch.forEach(function(call) {
|
||||
invariant(
|
||||
call.module === 'BatchedBridge',
|
||||
'All the calls should pass through the BatchedBridge module'
|
||||
);
|
||||
if (call.method === 'callFunctionReturnFlushedQueue') {
|
||||
self._callFunction.apply(self, call.args);
|
||||
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
||||
self._invokeCallback.apply(self, call.args);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.flushedQueue();
|
||||
}, null, this._flushedQueueUnguarded, this);
|
||||
},
|
||||
|
||||
setLoggingEnabled: function(enabled) {
|
||||
|
@ -80,15 +80,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||
__attribute__((used, section("__DATA,RCTImport"))) \
|
||||
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.
|
||||
*/
|
||||
@ -122,14 +113,4 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
||||
*/
|
||||
- (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
|
||||
|
@ -25,6 +25,7 @@
|
||||
#import "RCTProfile.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTSourceCode.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@ -45,12 +46,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporarily allow to turn on and off the call batching in case someone wants
|
||||
* to profile both
|
||||
*/
|
||||
#define BATCHED_BRIDGE 0
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTHeaderValue;
|
||||
typedef struct section_64 RCTHeaderSection;
|
||||
@ -61,6 +56,11 @@ typedef struct section RCTHeaderSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#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 RCTDequeueNotification = @"RCTDequeueNotification";
|
||||
|
||||
@ -122,6 +122,7 @@ static NSArray *RCTJSMethods(void)
|
||||
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
||||
* functionality is disabled in release mode to improve startup performance.
|
||||
*/
|
||||
static NSDictionary *RCTModuleIDsByName;
|
||||
static NSArray *RCTModuleNamesByID;
|
||||
static NSArray *RCTModuleClassesByID;
|
||||
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
@ -129,8 +130,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
RCTModuleNamesByID = [NSMutableArray array];
|
||||
RCTModuleClassesByID = [NSMutableArray array];
|
||||
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
||||
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
||||
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
||||
|
||||
Dl_info info;
|
||||
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
||||
@ -162,7 +164,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
NSStringFromClass(cls));
|
||||
|
||||
// Register module
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:RCTBridgeModuleNameForClass(cls)];
|
||||
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
||||
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
||||
}
|
||||
}
|
||||
@ -199,20 +203,31 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
return RCTModuleClassesByID;
|
||||
}
|
||||
|
||||
@class RCTBatchedBridge;
|
||||
|
||||
@interface RCTBridge ()
|
||||
|
||||
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
||||
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
||||
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
|
||||
#if BATCHED_BRIDGE
|
||||
@end
|
||||
|
||||
@interface RCTBatchedBridge : RCTBridge <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||
|
||||
- (instancetype)initWithParentBridge:(RCTBridge *)bridge;
|
||||
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@ -238,8 +253,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
dispatch_block_t _methodQueue;
|
||||
}
|
||||
|
||||
static Class _globalExecutorClass;
|
||||
|
||||
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
||||
{
|
||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||
@ -702,6 +715,7 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||
@"methods": [[NSMutableDictionary alloc] init]
|
||||
};
|
||||
localModules[moduleName] = module;
|
||||
[RCTLocalModuleNames addObject:moduleName];
|
||||
}
|
||||
|
||||
// Add method if it doesn't already exist
|
||||
@ -712,72 +726,18 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||
@"methodID": @(methods.count),
|
||||
@"type": @"local"
|
||||
};
|
||||
[RCTLocalMethodNames addObject:methodName];
|
||||
}
|
||||
|
||||
// Add module and method lookup
|
||||
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
||||
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||
[RCTLocalModuleNames addObject:moduleName];
|
||||
[RCTLocalMethodNames addObject:methodName];
|
||||
}
|
||||
});
|
||||
|
||||
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)
|
||||
|
||||
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
|
||||
@ -798,22 +758,6 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||
@end
|
||||
|
||||
@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;
|
||||
|
||||
@ -821,36 +765,226 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
/**
|
||||
* Pre register modules
|
||||
*/
|
||||
RCTLocalModulesConfig();
|
||||
|
||||
_bundleURL = bundleURL;
|
||||
_moduleProvider = block;
|
||||
_launchOptions = [launchOptions copy];
|
||||
|
||||
[self setUp];
|
||||
[self bindKeys];
|
||||
[self setUp];
|
||||
}
|
||||
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
|
||||
{
|
||||
Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class];
|
||||
_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];
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)];
|
||||
}];
|
||||
_vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)];
|
||||
_batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
|
||||
}
|
||||
|
||||
- (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
|
||||
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;
|
||||
}
|
||||
|
||||
@ -897,7 +1031,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
|
||||
// Get method queues
|
||||
_queuesByID = [[RCTSparseArray alloc] init];
|
||||
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
||||
if ([module respondsToSelector:@selector(methodQueue)]) {
|
||||
dispatch_queue_t queue = [module methodQueue];
|
||||
@ -907,7 +1040,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
_queuesByID[moduleID] = [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||
[_frameUpdateObservers addObject:module];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)initJS
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
// Inject module data into JS context
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
@ -920,7 +1062,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
_loading = YES;
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
|
||||
|
||||
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||
if (_javaScriptExecutor == nil) {
|
||||
|
||||
/**
|
||||
@ -929,11 +1073,17 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
*/
|
||||
_loading = NO;
|
||||
|
||||
} else if (_bundleURL) { // Allow testing without a script
|
||||
} else if (bundleURL) { // Allow testing without a script
|
||||
|
||||
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
||||
[loader loadBundleAtURL:_bundleURL onComplete:^(NSError *error) {
|
||||
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
||||
_loading = NO;
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = bundleURL;
|
||||
sourceCodeModule.scriptText = script;
|
||||
if (error != nil) {
|
||||
|
||||
NSArray *stack = [[error userInfo] objectForKey:@"stack"];
|
||||
@ -946,37 +1096,25 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
|
||||
} else {
|
||||
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:self];
|
||||
if (!loadError) {
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
||||
@ -985,53 +1123,48 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return _modulesByName;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
#pragma mark - RCTInvalidating
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _javaScriptExecutor != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (!self.isValid && _modulesByID == nil) {
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (![NSThread isMainThread]) {
|
||||
[self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES];
|
||||
return;
|
||||
}
|
||||
RCTAssertMainThread();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Release executor
|
||||
_valid = NO;
|
||||
if (_latestJSExecutor == _javaScriptExecutor) {
|
||||
_latestJSExecutor = nil;
|
||||
}
|
||||
[_javaScriptExecutor invalidate];
|
||||
_javaScriptExecutor = nil;
|
||||
|
||||
[_displayLink invalidate];
|
||||
[_vsyncDisplayLink invalidate];
|
||||
_frameUpdateObservers = nil;
|
||||
/**
|
||||
* Main Thread deallocations
|
||||
*/
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_mainDisplayLink invalidate];
|
||||
|
||||
// Invalidate modules
|
||||
for (id target in _modulesByID.allObjects) {
|
||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||
[(id<RCTInvalidating>)target invalidate];
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
/**
|
||||
* JS Thread deallocations
|
||||
*/
|
||||
[_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)
|
||||
_modulesByID = nil;
|
||||
_queuesByID = nil;
|
||||
_modulesByName = nil;
|
||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||
_javaScriptExecutor = nil;
|
||||
_frameUpdateObservers = nil;
|
||||
_modulesByID = nil;
|
||||
_queuesByID = nil;
|
||||
_modulesByName = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1066,6 +1199,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
*/
|
||||
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
NSString *moduleDotMethod = @"RCTJSTimers.callTimers";
|
||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
||||
@ -1074,35 +1209,29 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
||||
|
||||
if (!_loading) {
|
||||
#if BATCHED_BRIDGE
|
||||
dispatch_block_t block = ^{
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
};
|
||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
}
|
||||
dispatch_block_t block = ^{
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
#endif
|
||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
||||
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
||||
RCTAssertJSThread();
|
||||
|
||||
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
|
||||
if (scriptLoadError) {
|
||||
onComplete(scriptLoadError);
|
||||
@ -1132,7 +1261,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (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]) {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
@ -1146,13 +1281,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
*/
|
||||
- (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:^{
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
RCTBridge *strongSelf = weakSelf;
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
||||
return;
|
||||
}
|
||||
@ -1190,7 +1327,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
*/
|
||||
if (
|
||||
[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];
|
||||
}
|
||||
@ -1218,10 +1355,14 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||
{
|
||||
#endif
|
||||
RCTAssertJSThread();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||
[self _handleBuffer:json context:context];
|
||||
};
|
||||
@ -1237,6 +1378,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
if (buffer == nil || buffer == (id)kCFNull) {
|
||||
return;
|
||||
}
|
||||
@ -1307,6 +1450,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
params:(NSArray *)params
|
||||
context:(NSNumber *)context
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
if (!self.isValid) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
@ -1330,10 +1478,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return NO;
|
||||
}
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[self dispatchBlock:^{
|
||||
RCTProfileBeginEvent();
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
|
||||
if (!strongSelf.isValid) {
|
||||
// strongSelf has been invalidated since the dispatch_async call and this
|
||||
@ -1367,18 +1515,21 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
||||
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
||||
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
|
||||
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];
|
||||
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
||||
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
||||
@ -1393,67 +1544,41 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||
}
|
||||
|
||||
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
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
|
||||
{
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
|
||||
RCTProfileInit();
|
||||
}
|
||||
|
||||
- (void)stopProfiling
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
|
||||
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];
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
|
@ -123,10 +123,12 @@ RCT_EXPORT_MODULE()
|
||||
{
|
||||
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
|
||||
|
||||
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)jsLoaded
|
||||
@ -147,10 +149,12 @@ RCT_EXPORT_MODULE()
|
||||
_liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
||||
}
|
||||
|
||||
// Hit these setters again after bridge has finished loading
|
||||
self.profilingEnabled = _profilingEnabled;
|
||||
self.liveReloadEnabled = _liveReloadEnabled;
|
||||
self.executorClass = _executorClass;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Hit these setters again after bridge has finished loading
|
||||
self.profilingEnabled = _profilingEnabled;
|
||||
self.liveReloadEnabled = _liveReloadEnabled;
|
||||
self.executorClass = _executorClass;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
@ -22,6 +22,6 @@
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete;
|
||||
|
||||
@end
|
||||
|
@ -27,7 +27,7 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete
|
||||
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete
|
||||
{
|
||||
// Sanitize the script URL
|
||||
scriptURL = [RCTConvert NSURL:scriptURL.absoluteString];
|
||||
@ -37,7 +37,7 @@
|
||||
NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{
|
||||
NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided"
|
||||
}];
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
code:error.code
|
||||
userInfo:userInfo];
|
||||
}
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -96,18 +96,10 @@
|
||||
code:[(NSHTTPURLResponse *)response statusCode]
|
||||
userInfo:userInfo];
|
||||
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = scriptURL;
|
||||
sourceCodeModule.scriptText = rawText;
|
||||
|
||||
[_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
onComplete(scriptError);
|
||||
});
|
||||
}];
|
||||
onComplete(nil, rawText);
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
#import "RCTBridge.h"
|
||||
|
||||
@interface RCTRootView : UIView <RCTInvalidating>
|
||||
@interface RCTRootView : UIView
|
||||
|
||||
/**
|
||||
* - Designated initializer -
|
||||
|
@ -20,25 +20,37 @@
|
||||
#import "RCTTouchHandler.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTWebViewExecutor.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@interface RCTBridge (RCTRootView)
|
||||
|
||||
@property (nonatomic, weak, readonly) RCTBridge *batchedBridge;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTUIManager (RCTRootView)
|
||||
|
||||
- (NSNumber *)allocateRootTag;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRootContentView : RCTView <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
RCTBridge *_bridge;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
NSString *_moduleName;
|
||||
NSDictionary *_launchOptions;
|
||||
UIView *_contentView;
|
||||
RCTRootContentView *_contentView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
{
|
||||
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
|
||||
@ -52,11 +64,11 @@
|
||||
_moduleName = moduleName;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(bundleFinishedLoading)
|
||||
selector:@selector(javaScriptDidLoad:)
|
||||
name:RCTJavaScriptDidLoadNotification
|
||||
object:_bridge];
|
||||
if (!_bridge.loading) {
|
||||
[self bundleFinishedLoading];
|
||||
if (!_bridge.batchedBridge.isLoading) {
|
||||
[self bundleFinishedLoading:_bridge.batchedBridge];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
@ -73,25 +85,6 @@
|
||||
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
|
||||
{
|
||||
return _backingViewController ?: [super backingViewController];
|
||||
@ -105,9 +98,19 @@
|
||||
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
||||
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(), ^{
|
||||
if (!bridge.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
[_contentView removeFromSuperview];
|
||||
_contentView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
_contentView.reactTag = [_bridge.uiManager allocateRootTag];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||
[_contentView addGestureRecognizer:_touchHandler];
|
||||
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
|
||||
bridge:bridge];
|
||||
[self addSubview:_contentView];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": _contentView.reactTag,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
@"initialProps": _initialProperties ?: @{},
|
||||
};
|
||||
[_bridge.uiManager registerRootView:_contentView];
|
||||
[_bridge enqueueJSCall:@"AppRegistry.runApplication"
|
||||
[bridge enqueueJSCall:@"AppRegistry.runApplication"
|
||||
args:@[moduleName, appParameters]];
|
||||
});
|
||||
}
|
||||
@ -139,7 +139,6 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||
[super layoutSubviews];
|
||||
if (_contentView) {
|
||||
_contentView.frame = self.bounds;
|
||||
[_bridge.uiManager setFrame:self.bounds forRootView:_contentView];
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +147,12 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||
return _contentView.reactTag;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_contentView removeFromSuperview];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUIManager (RCTRootView)
|
||||
@ -160,3 +165,60 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||
}
|
||||
|
||||
@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 paused = _paused;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@ -78,7 +79,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_paused = YES;
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
|
||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||
@ -126,7 +127,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
||||
|
||||
- (void)stopTimers
|
||||
{
|
||||
[_bridge removeFrameUpdateObserver:self];
|
||||
_paused = YES;
|
||||
}
|
||||
|
||||
- (void)startTimers
|
||||
@ -135,7 +136,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
||||
return;
|
||||
}
|
||||
|
||||
[_bridge addFrameUpdateObserver:self];
|
||||
_paused = NO;
|
||||
}
|
||||
|
||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
|
@ -267,11 +267,6 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _viewRegistry != nil;
|
||||
@ -279,20 +274,24 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
/**
|
||||
* Called on the JS Thread since all modules are invalidated on the JS thread
|
||||
*/
|
||||
|
||||
for (NSNumber *rootViewTag in _rootViewTags) {
|
||||
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
for (NSNumber *rootViewTag in _rootViewTags) {
|
||||
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
_rootViewTags = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
_viewRegistry = nil;
|
||||
_bridge = nil;
|
||||
_rootViewTags = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
_viewRegistry = nil;
|
||||
_bridge = nil;
|
||||
|
||||
[_pendingUIBlocksLock lock];
|
||||
_pendingUIBlocks = nil;
|
||||
[_pendingUIBlocksLock unlock];
|
||||
[_pendingUIBlocksLock lock];
|
||||
_pendingUIBlocks = nil;
|
||||
[_pendingUIBlocksLock unlock];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
|
@ -264,9 +264,13 @@ NSInteger kNeverProgressed = -10000;
|
||||
NSInteger _numberOfViewControllerMovesToIgnore;
|
||||
}
|
||||
|
||||
@synthesize paused = _paused;
|
||||
|
||||
- (id)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
_paused = YES;
|
||||
|
||||
_bridge = bridge;
|
||||
_mostRecentProgress = kNeverProgressed;
|
||||
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
@ -341,14 +345,14 @@ NSInteger kNeverProgressed = -10000;
|
||||
_dummyView.frame = (CGRect){{destination}};
|
||||
_currentlyTransitioningFrom = indexOfFrom;
|
||||
_currentlyTransitioningTo = indexOfTo;
|
||||
[_bridge addFrameUpdateObserver:self];
|
||||
_paused = NO;
|
||||
}
|
||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
[weakSelf freeLock];
|
||||
_currentlyTransitioningFrom = 0;
|
||||
_currentlyTransitioningTo = 0;
|
||||
_dummyView.frame = CGRectZero;
|
||||
[_bridge removeFrameUpdateObserver:self];
|
||||
_paused = YES;
|
||||
// Reset the parallel position tracker
|
||||
}];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user