2015-02-02 updates
- Removed special-case treatment of RCTTiming and RCTUIManager modules | Nick Lockwood
This commit is contained in:
parent
00f0ebccdf
commit
ccd8f184af
|
@ -6,33 +6,8 @@
|
|||
|
||||
@protocol RCTNativeModule;
|
||||
|
||||
@class RCTUIManager;
|
||||
@class RCTEventDispatcher;
|
||||
|
||||
/**
|
||||
* Functions are the one thing that aren't automatically converted to OBJC
|
||||
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
|
||||
* They must be expressed as `JSValue`s.
|
||||
*
|
||||
* But storing callbacks causes reference cycles!
|
||||
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
|
||||
* We'll live with the leak for now, but need to clean this up asap:
|
||||
* Passing a reference to the `context` to the bridge would make it easy to
|
||||
* execute JS. We can add `JSManagedValue`s to protect against this. The same
|
||||
* needs to be done in `RCTTiming` and friends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Must be kept in sync with `MessageQueue.js`.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldRequestModuleIDs = 0,
|
||||
RCTBridgeFieldMethodIDs,
|
||||
RCTBridgeFieldParamss,
|
||||
RCTBridgeFieldResponseCBIDs,
|
||||
RCTBridgeFieldResponseReturnValues,
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
@class RCTRootView;
|
||||
|
||||
/**
|
||||
* Utilities for constructing common response objects. When sending a
|
||||
|
@ -59,19 +34,19 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
|||
@interface RCTBridge : NSObject <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
|
||||
|
||||
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
- (void)enqueueUpdateTimers;
|
||||
|
||||
@property (nonatomic, readonly) RCTUIManager *uiManager;
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||
|
||||
// For use in implementing delegates, which may need to queue responses.
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* Global logging function will print to both xcode and js debugger consoles.
|
||||
*
|
||||
|
|
|
@ -9,11 +9,34 @@
|
|||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleIDs.h"
|
||||
#import "RCTTiming.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *RCTModuleName(Class moduleClass)
|
||||
/**
|
||||
* Functions are the one thing that aren't automatically converted to OBJC
|
||||
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
|
||||
* They must be expressed as `JSValue`s.
|
||||
*
|
||||
* But storing callbacks causes reference cycles!
|
||||
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
|
||||
* We'll live with the leak for now, but need to clean this up asap:
|
||||
* Passing a reference to the `context` to the bridge would make it easy to
|
||||
* execute JS. We can add `JSManagedValue`s to protect against this. The same
|
||||
* needs to be done in `RCTTiming` and friends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Must be kept in sync with `MessageQueue.js`.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldRequestModuleIDs = 0,
|
||||
RCTBridgeFieldMethodIDs,
|
||||
RCTBridgeFieldParamss,
|
||||
RCTBridgeFieldResponseCBIDs,
|
||||
RCTBridgeFieldResponseReturnValues,
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
static NSString *RCTModuleName(Class moduleClass)
|
||||
{
|
||||
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||
|
||||
|
@ -22,22 +45,11 @@ NSString *RCTModuleName(Class moduleClass)
|
|||
} else {
|
||||
|
||||
// Default implementation, works in most cases
|
||||
NSString *className = NSStringFromClass(moduleClass);
|
||||
|
||||
// TODO: be more consistent with naming so that this check isn't needed
|
||||
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
if ([className hasPrefix:@"RCTUI"]) {
|
||||
className = [className substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([className hasSuffix:@"Manager"]) {
|
||||
className = [className substringToIndex:className.length - @"Manager".length];
|
||||
}
|
||||
}
|
||||
return className;
|
||||
return NSStringFromClass(moduleClass);
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *RCTNativeModuleClasses(void)
|
||||
static NSDictionary *RCTNativeModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
|
@ -79,47 +91,31 @@ NSDictionary *RCTNativeModuleClasses(void)
|
|||
{
|
||||
NSMutableDictionary *_moduleInstances;
|
||||
NSDictionary *_javaScriptModulesConfig;
|
||||
dispatch_queue_t _shadowQueue;
|
||||
RCTTiming *_timing;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
}
|
||||
|
||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
shadowQueue:(dispatch_queue_t)shadowQueue
|
||||
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_javaScriptExecutor = javaScriptExecutor;
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_shadowQueue = shadowQueue;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// TODO (#5906496): Remove special case
|
||||
_timing = [[RCTTiming alloc] initWithBridge:self];
|
||||
_javaScriptModulesConfig = javaScriptModulesConfig;
|
||||
_moduleInstances[RCTModuleName([RCTTiming class])] = _timing;
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// TODO (#5906496): Remove special case
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
viewManagers[moduleName] = [[moduleClass alloc] init];
|
||||
}
|
||||
}];
|
||||
_uiManager = [[RCTUIManager alloc] initWithShadowQueue:_shadowQueue viewManagers:viewManagers];
|
||||
_uiManager.eventDispatcher = _eventDispatcher;
|
||||
_moduleInstances[RCTModuleName([RCTUIManager class])] = _uiManager;
|
||||
[_moduleInstances addEntriesFromDictionary:viewManagers];
|
||||
|
||||
// Register remaining modules
|
||||
// Register modules
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if (_moduleInstances[moduleName] == nil) {
|
||||
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
||||
} else {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] init];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[self doneRegisteringModules];
|
||||
|
@ -148,8 +144,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
_javaScriptExecutor = nil;
|
||||
|
||||
dispatch_sync(_shadowQueue, ^{
|
||||
// Make sure all dispatchers have been executed before
|
||||
// freeing up memory from _asyncHookMapByModuleID
|
||||
// Make sure all dispatchers have been executed before continuing
|
||||
// TODO: is this still needed?
|
||||
});
|
||||
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
|
@ -158,8 +154,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
}
|
||||
[_moduleInstances removeAllObjects];
|
||||
|
||||
_timing = nil;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,11 +193,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)enqueueUpdateTimers
|
||||
{
|
||||
[_timing enqueueUpdateTimers];
|
||||
}
|
||||
|
||||
#pragma mark - Payload Generation
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||
|
@ -274,12 +263,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
}
|
||||
|
||||
NSArray *moduleIDs = [requestsArray objectAtIndex:RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = [requestsArray objectAtIndex:RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramss = [requestsArray objectAtIndex:RCTBridgeFieldParamss];
|
||||
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||
|
||||
NSUInteger numRequests = [moduleIDs count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramss count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||
if (!allSame) {
|
||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
return;
|
||||
|
@ -288,74 +277,32 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||
@autoreleasepool {
|
||||
[self _handleRequestNumber:i
|
||||
moduleID:[moduleIDs objectAtIndex:i]
|
||||
methodID:[methodIDs objectAtIndex:i]
|
||||
params:[paramss objectAtIndex:i]];
|
||||
moduleID:[moduleIDs[i] integerValue]
|
||||
methodID:[methodIDs[i] integerValue]
|
||||
params:paramsArrays[i]];
|
||||
}
|
||||
}
|
||||
|
||||
// Update modules
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(batchDidComplete)]) {
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
[target batchDidComplete];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_handleRequestNumber:(NSUInteger)i moduleID:(id)moduleID methodID:(id)methodID params:(id)params
|
||||
{
|
||||
if (![moduleID isKindOfClass:[NSNumber class]] || ![methodID isKindOfClass:[NSNumber class]] || ![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
return;
|
||||
}
|
||||
[self _dispatchUsingAsyncHookMapWithModuleID:[moduleID integerValue]
|
||||
methodID:[methodID integerValue]
|
||||
params:params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callback that reports values back to the JS thread.
|
||||
* TODO (#5906496): These responses should go into their own queue `MessageQueue.m` that
|
||||
* mirrors the JS queue and protocol. For now, we speak the "language" of the JS
|
||||
* queue by packing it into an array that matches the wire protocol.
|
||||
*/
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)cbID
|
||||
{
|
||||
if (!cbID) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return ^(NSArray *args) {
|
||||
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSInvocation *)invocationForAdditionalArguments:(NSUInteger)argCount
|
||||
{
|
||||
static NSMutableDictionary *invocations;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
invocations = [NSMutableDictionary dictionary];
|
||||
});
|
||||
|
||||
id key = @(argCount);
|
||||
NSInvocation *invocation = invocations[key];
|
||||
if (invocation == nil) {
|
||||
NSString *objCTypes = [@"v@:" stringByPaddingToLength:3 + argCount withString:@"@" startingAtIndex:0];
|
||||
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:objCTypes.UTF8String];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
invocations[key] = invocation;
|
||||
}
|
||||
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (BOOL)_dispatchUsingAsyncHookMapWithModuleID:(NSInteger)moduleID
|
||||
- (BOOL)_handleRequestNumber:(NSUInteger)i
|
||||
moduleID:(NSInteger)moduleID
|
||||
methodID:(NSInteger)methodID
|
||||
params:(NSArray *)params
|
||||
{
|
||||
if (![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
|
||||
return NO;
|
||||
}
|
||||
|
@ -369,13 +316,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
RCTModuleMethod *method = methods[methodID];
|
||||
NSUInteger methodArity = method.arity;
|
||||
if (params.count != methodArity) {
|
||||
RCTLogMustFix(
|
||||
@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
RCTLogMustFix(@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
moduleName,
|
||||
method.JSMethodName
|
||||
);
|
||||
method.JSMethodName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
@ -430,6 +375,43 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callback that reports values back to the JS thread.
|
||||
* TODO (#5906496): These responses should go into their own queue `MessageQueue.m` that
|
||||
* mirrors the JS queue and protocol. For now, we speak the "language" of the JS
|
||||
* queue by packing it into an array that matches the wire protocol.
|
||||
*/
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)cbID
|
||||
{
|
||||
if (!cbID) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return ^(NSArray *args) {
|
||||
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSInvocation *)invocationForAdditionalArguments:(NSUInteger)argCount
|
||||
{
|
||||
static NSMutableDictionary *invocations;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
invocations = [NSMutableDictionary dictionary];
|
||||
});
|
||||
|
||||
id key = @(argCount);
|
||||
NSInvocation *invocation = invocations[key];
|
||||
if (invocation == nil) {
|
||||
NSString *objCTypes = [@"v@:" stringByPaddingToLength:3 + argCount withString:@"@" startingAtIndex:0];
|
||||
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:objCTypes.UTF8String];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
invocations[key] = invocation;
|
||||
}
|
||||
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (void)doneRegisteringModules
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
@ -456,8 +438,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
moduleConfig[@"methods"] = methods;
|
||||
|
||||
id target = [_moduleInstances objectForKey:moduleName];
|
||||
if ([target respondsToSelector:@selector(constantsToExport)] && ![target conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
// TODO: find a more elegant way to handle RCTNativeViewModule constants as a special case
|
||||
if ([target respondsToSelector:@selector(constantsToExport)]) {
|
||||
moduleConfig[@"constants"] = [target constantsToExport];
|
||||
}
|
||||
moduleConfigs[moduleName] = moduleConfig;
|
||||
|
@ -484,6 +465,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
for (id target in _moduleInstances.objectEnumerator) {
|
||||
if ([target respondsToSelector:@selector(registerRootView:)]) {
|
||||
[target registerRootView:rootView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)hasValidJSExecutor
|
||||
{
|
||||
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "RCTLog.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTSparseArray;
|
||||
@class RCTUIManager;
|
||||
|
||||
|
@ -42,6 +43,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
|||
@protocol RCTNativeModule <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optional initializer for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Place this macro inside the method body of any method you want
|
||||
* to expose to JS. The optional js_name argument will be used as
|
||||
|
@ -74,7 +81,7 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
|||
/**
|
||||
* Provides minimal interface needed to register a UIViewManager module
|
||||
*/
|
||||
@protocol RCTNativeViewModule <RCTNativeModule>
|
||||
@protocol RCTNativeViewModule <NSObject>
|
||||
|
||||
/**
|
||||
* This method instantiates a native view to be managed by the module.
|
||||
|
@ -83,6 +90,12 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
|
|||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
* automatically by using the view module's class name.
|
||||
*/
|
||||
+ (NSString *)moduleName;
|
||||
|
||||
/**
|
||||
* This method instantiates a shadow view to be managed by the module. If omitted,
|
||||
* an ordinary RCTShadowView instance will be created.
|
||||
|
@ -167,6 +180,12 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
|||
*/
|
||||
- (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
*/
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
* JavaScript so that applications can clean up resources. (launch blocker).
|
||||
* TODO: Incremental module loading. (low pri).
|
||||
*/
|
||||
@interface RCTJavaScriptAppEngine : NSObject <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, readonly, strong) RCTBridge *bridge;
|
||||
@interface RCTJavaScriptAppEngine : NSObject
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
|
|
@ -22,11 +22,8 @@
|
|||
*/
|
||||
@implementation RCTJavaScriptAppEngine
|
||||
{
|
||||
BOOL _isPaused; // Pauses drawing/updating of the JSView
|
||||
BOOL _pauseOnEnterBackground;
|
||||
CADisplayLink *_displayLink;
|
||||
NSTimer *_runTimer;
|
||||
NSDictionary *_loadedResource;
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
|
@ -54,118 +51,13 @@
|
|||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_isPaused = NO;
|
||||
self.pauseOnEnterBackground = YES;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(run:)];
|
||||
if (_displayLink) {
|
||||
[_displayLink setFrameInterval:1];
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
} else {
|
||||
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
|
||||
_runTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:@selector(run:) userInfo:nil repeats:YES];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Wait until operations on `javaScriptQueue` are complete.
|
||||
*/
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"-invalidate must be called before -dealloc");
|
||||
}
|
||||
|
||||
#pragma mark - RCTInvalidating
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _displayLink != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_bridge invalidate];
|
||||
_bridge = nil;
|
||||
|
||||
[_displayLink invalidate];
|
||||
_displayLink = nil;
|
||||
|
||||
// Remove from notification center
|
||||
self.pauseOnEnterBackground = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Run loop
|
||||
|
||||
- (void)run:(CADisplayLink *)sender
|
||||
{
|
||||
if (!_isPaused) {
|
||||
RCTAssertMainThread();
|
||||
[_bridge enqueueUpdateTimers];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pauseRunLoop
|
||||
{
|
||||
if (!_isPaused) {
|
||||
[_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
_isPaused = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resumeRunLoop
|
||||
{
|
||||
if (_isPaused) {
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
_isPaused = NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See warnings from lint: UIApplicationDidBecomeActive fires in a critical
|
||||
* foreground path, and prevents the app from prioritizing the foreground
|
||||
* processing. Consider using
|
||||
* FBApplicationDidFinishEnteringForegroundAndIsNowIdleNotification.
|
||||
*/
|
||||
- (void)setPauseOnEnterBackground:(BOOL)pauses
|
||||
{
|
||||
NSArray *pauseN = @[
|
||||
UIApplicationWillResignActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationWillTerminateNotification
|
||||
];
|
||||
NSArray *resumeN =
|
||||
@[UIApplicationWillEnterForegroundNotification, UIApplicationDidBecomeActiveNotification];
|
||||
|
||||
if (pauses) {
|
||||
[self observeKeyPaths:pauseN selector:@selector(pauseRunLoop)];
|
||||
[self observeKeyPaths:resumeN selector:@selector(resumeRunLoop)];
|
||||
}
|
||||
else {
|
||||
[self removeObserverForKeyPaths:pauseN];
|
||||
[self removeObserverForKeyPaths:resumeN];
|
||||
}
|
||||
_pauseOnEnterBackground = pauses;
|
||||
}
|
||||
|
||||
- (void)removeObserverForKeyPaths:(NSArray*)keyPaths
|
||||
{
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
for (NSString *name in keyPaths) {
|
||||
[nc removeObserver:self name:name object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeKeyPaths:(NSArray*)keyPaths selector:(SEL)selector
|
||||
{
|
||||
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
||||
for (NSString *name in keyPaths) {
|
||||
[nc addObserver:self selector:selector name:name object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Module and script loading
|
||||
|
||||
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL
|
||||
|
|
|
@ -18,7 +18,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
|||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
dispatch_queue_t _shadowQueue;
|
||||
RCTBridge *_bridge;
|
||||
RCTJavaScriptAppEngine *_appEngine;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
|
@ -62,9 +61,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
|||
|
||||
- (void)setUp
|
||||
{
|
||||
// TODO: does it make sense to do this here? What if there's more than one host view?
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Every root view that is created must have a unique react tag.
|
||||
// Numbering of these tags goes from 1, 11, 21, 31, etc
|
||||
static NSInteger rootViewTag = 1;
|
||||
|
@ -95,14 +91,14 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
|||
}
|
||||
} else {
|
||||
|
||||
[_bridge.uiManager registerRootView:self];
|
||||
[_bridge registerRootView:self];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": self.reactTag ?: @0,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
};
|
||||
[_appEngine.bridge enqueueJSCall:RCTModuleIDBundler
|
||||
[_bridge enqueueJSCall:RCTModuleIDBundler
|
||||
methodID:RCTBundlerRunApplication
|
||||
args:@[moduleName, appParameters]];
|
||||
}
|
||||
|
@ -125,12 +121,10 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
|
|||
};
|
||||
|
||||
[_executor invalidate];
|
||||
[_appEngine invalidate];
|
||||
[_bridge invalidate];
|
||||
|
||||
_executor = [[RCTContextExecutor alloc] init];
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
|
||||
shadowQueue:_shadowQueue
|
||||
javaScriptModulesConfig:[RCTModuleIDs config]];
|
||||
|
||||
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTExport.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
@interface RCTTiming : NSObject <RCTNativeModule>
|
||||
@interface RCTTiming : NSObject <RCTNativeModule, RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (void)enqueueUpdateTimers;
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
@interface RCTTimer : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) NSDate *target;
|
||||
@property (nonatomic, assign, readonly, getter=isActive) BOOL active;
|
||||
@property (nonatomic, assign, readonly) BOOL repeats;
|
||||
@property (nonatomic, strong, readonly) NSNumber *callbackID;
|
||||
@property (nonatomic, assign, readonly) NSTimeInterval interval;
|
||||
|
@ -26,7 +25,6 @@
|
|||
repeats:(BOOL)repeats
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_active = YES;
|
||||
_interval = interval;
|
||||
_repeats = repeats;
|
||||
_callbackID = callbackID;
|
||||
|
@ -40,13 +38,9 @@
|
|||
*/
|
||||
- (BOOL)updateFoundNeedsJSUpdate
|
||||
{
|
||||
if (_active && _target.timeIntervalSinceNow <= 0) {
|
||||
if (_target && _target.timeIntervalSinceNow <= 0) {
|
||||
// The JS Timers will do fine grained calculating of expired timeouts.
|
||||
if (_repeats) {
|
||||
_target = [NSDate dateWithTimeIntervalSinceNow:_interval];
|
||||
} else {
|
||||
_active = NO;
|
||||
}
|
||||
_target = _repeats ? [NSDate dateWithTimeIntervalSinceNow:_interval] : nil;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
|
@ -58,6 +52,7 @@
|
|||
{
|
||||
RCTSparseArray *_timers;
|
||||
RCTBridge *_bridge;
|
||||
id _updateTimer;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
|
@ -65,19 +60,74 @@
|
|||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
[self startTimers];
|
||||
|
||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationWillTerminateNotification]) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(stopTimers)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationWillEnterForegroundNotification]) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(startTimers)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (#5906496): Wait until operations on `javaScriptQueue` are complete to complete the
|
||||
* `dealloc`.
|
||||
*/
|
||||
/* - (void)dealloc
|
||||
- (void)dealloc
|
||||
{
|
||||
} */
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)enqueueUpdateTimers
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _bridge != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[self stopTimers];
|
||||
_bridge = nil;
|
||||
}
|
||||
|
||||
- (void)stopTimers
|
||||
{
|
||||
[_updateTimer invalidate];
|
||||
_updateTimer = nil;
|
||||
}
|
||||
|
||||
- (void)startTimers
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (![self isValid] || _updateTimer != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
_updateTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
|
||||
if (_updateTimer) {
|
||||
[_updateTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
} else {
|
||||
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
|
||||
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60)
|
||||
target:self
|
||||
selector:@selector(update)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)update
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
|
@ -86,7 +136,7 @@
|
|||
if ([timer updateFoundNeedsJSUpdate]) {
|
||||
[timersToCall addObject:timer.callbackID];
|
||||
}
|
||||
if (!timer.active) {
|
||||
if (!timer.target) {
|
||||
_timers[timer.callbackID] = nil;
|
||||
}
|
||||
}
|
||||
|
@ -97,14 +147,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)scheduleCallbackID:(NSNumber *)callbackID interval:(NSTimeInterval)interval targetTime:(NSTimeInterval)targetTime repeats:(BOOL)repeats
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID interval:interval targetTime:targetTime repeats:repeats];
|
||||
_timers[callbackID] = timer;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* There's a small difference between the time when we call
|
||||
* setTimeout/setInterval/requestAnimation frame and the time it actually makes
|
||||
|
@ -132,10 +174,13 @@
|
|||
interval = 0;
|
||||
}
|
||||
|
||||
[self scheduleCallbackID:callbackID
|
||||
RCTTimer *timer = [[RCTTimer alloc] initWithCallbackID:callbackID
|
||||
interval:interval
|
||||
targetTime:targetTime
|
||||
repeats:repeats.boolValue];
|
||||
repeats:repeats];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_timers[callbackID] = timer;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)deleteTimer:(NSNumber *)timerID
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#import "RCTExport.h"
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
@class RCTAnimationRegistry;
|
||||
@class RCTBridge;
|
||||
@class RCTRootView;
|
||||
@class RCTShadowView;
|
||||
|
||||
|
@ -14,13 +14,10 @@
|
|||
|
||||
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
|
||||
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
viewManagers:(NSDictionary *)viewManagers;
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
@property (nonatomic, strong) RCTEventDispatcher *eventDispatcher;
|
||||
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
|
||||
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
|
||||
@property (nonatomic, strong) RCTAnimationRegistry *animationRegistry;
|
||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,64 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
}
|
||||
}
|
||||
|
||||
static NSString *RCTModuleName(Class moduleClass)
|
||||
{
|
||||
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
|
||||
|
||||
return [moduleClass moduleName];
|
||||
|
||||
} else {
|
||||
|
||||
// Default implementation, works in most cases
|
||||
NSString *className = NSStringFromClass(moduleClass);
|
||||
if ([className hasPrefix:@"RCTUI"]) {
|
||||
className = [className substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([className hasSuffix:@"Manager"]) {
|
||||
className = [className substringToIndex:className.length - @"Manager".length];
|
||||
}
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
static NSDictionary *RCTViewModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
modules = [NSMutableDictionary dictionary];
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
|
||||
Class cls = classes[i];
|
||||
|
||||
if (!class_getSuperclass(cls)) {
|
||||
// Class has no superclass - it's probably something weird
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![cls conformsToProtocol:@protocol(RCTNativeViewModule)]) {
|
||||
// Not an RCTNativeModule
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = RCTModuleName(cls);
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register RCTNativeViewModule class %@ for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
modules[moduleName] = cls;
|
||||
}
|
||||
|
||||
free(classes);
|
||||
});
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@implementation RCTUIManager
|
||||
{
|
||||
// Root views are only mutated on the shadow queue
|
||||
|
@ -42,13 +100,14 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
NSMutableArray *_pendingUIBlocks;
|
||||
|
||||
pthread_mutex_t _pendingUIBlocksMutex;
|
||||
dispatch_queue_t _shadowQueue;
|
||||
NSDictionary *_nextLayoutAnimationConfig; // RCT thread only
|
||||
RCTResponseSenderBlock _nextLayoutAnimationCallback; // RCT thread only
|
||||
RCTResponseSenderBlock _layoutAnimationCallbackMT; // Main thread only
|
||||
|
||||
NSMutableDictionary *_defaultShadowViews;
|
||||
NSMutableDictionary *_defaultViews;
|
||||
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (id <RCTNativeViewModule>)_managerInstanceForViewWithModuleName:(NSString *)moduleName
|
||||
|
@ -62,16 +121,22 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
return managerInstance;
|
||||
}
|
||||
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
viewManagers:(NSDictionary *)viewManagers
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_bridge = bridge;
|
||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
||||
|
||||
// Instantiate view managers
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
viewManagers[moduleName] = [[moduleClass alloc] init];
|
||||
}];
|
||||
_viewManagers = viewManagers;
|
||||
|
||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowQueue = shadowQueue;
|
||||
pthread_mutex_init(&_pendingUIBlocksMutex, NULL);
|
||||
|
||||
// Internal resources
|
||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||
|
@ -101,6 +166,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
|
||||
- (void)invalidate
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
_viewRegistry = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
|
||||
|
@ -111,6 +178,8 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
NSNumber *reactTag = rootView.reactTag;
|
||||
UIView *existingView = _viewRegistry[reactTag];
|
||||
RCTCAssert(existingView == nil || existingView == rootView,
|
||||
|
@ -121,7 +190,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
CGRect frame = rootView.frame;
|
||||
|
||||
// Register shadow view
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
dispatch_async(_bridge.shadowQueue, ^{
|
||||
|
||||
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
||||
shadowView.reactTag = reactTag;
|
||||
|
@ -468,7 +537,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
|||
|
||||
- (UIView *)viewForViewManager:(id <RCTNativeViewModule>)manager
|
||||
{
|
||||
return [manager viewWithEventDispatcher:_eventDispatcher];
|
||||
return [manager viewWithEventDispatcher:_bridge.eventDispatcher];
|
||||
}
|
||||
|
||||
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, id <RCTNativeViewModule>manager)
|
||||
|
|
Loading…
Reference in New Issue