Skip to content

Commit

Permalink
[Bridge] Scope the module loading and initialization to a single bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
ide committed Apr 7, 2015
1 parent b360168 commit b8271e9
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 73 deletions.
101 changes: 28 additions & 73 deletions React/Base/RCTBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import <mach-o/dyld.h>
#import <mach-o/getsect.h>

#import "RCTBridgeModuleLoader.h"
#import "RCTContextExecutor.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
Expand All @@ -41,14 +42,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {

NSString *const RCTReloadBridge = @"RCTReloadBridge";

/**
* This function returns the module name for a given class.
*/
static NSString *RCTModuleNameForClass(Class cls)
{
return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
}

/**
* This function scans all classes available at runtime and returns an array
* of all JSMethods registered.
Expand All @@ -72,38 +65,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
return JSMethods;
}

/**
* This function scans all classes available at runtime and returns an array
* of all classes that implement the RTCBridgeModule protocol.
*/
static NSArray *RCTModuleNamesByID;
static NSArray *RCTBridgeModuleClassesByModuleID(void)
{
static NSArray *modules;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
modules = [NSMutableArray array];
RCTModuleNamesByID = [NSMutableArray array];

RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
if ([cls conformsToProtocol:@protocol(RCTBridgeModule)]) {

// Add module
[(NSMutableArray *)modules addObject:cls];

// Add module name
NSString *moduleName = RCTModuleNameForClass(cls);
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
}
});

modules = [modules copy];
RCTModuleNamesByID = [RCTModuleNamesByID copy];
});

return modules;
}

@interface RCTBridge ()

- (void)_invokeAndProcessModule:(NSString *)module
Expand Down Expand Up @@ -302,7 +263,7 @@ - (NSString *)description
* generates an array of arrays of RCTModuleMethod objects, keyed
* by module index.
*/
static RCTSparseArray *RCTExportedMethodsByModuleID(void)
static RCTSparseArray *RCTExportedMethodsByModuleID(RCTBridgeModuleLoader *moduleLoader)
{
static RCTSparseArray *methodsByModuleID;
static dispatch_once_t onceToken;
Expand All @@ -328,7 +289,7 @@ - (NSString *)description
return;
}

NSArray *classes = RCTBridgeModuleClassesByModuleID();
NSArray *classes = moduleLoader.moduleClassesByModuleID;
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];

for (RCTExportValue addr = section->offset;
Expand Down Expand Up @@ -384,16 +345,16 @@ - (NSString *)description
* },
* etc...
*/
static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
static NSDictionary *RCTRemoteModulesConfig(RCTBridgeModuleLoader *moduleLoader, NSDictionary *modulesByName)
{
static NSMutableDictionary *remoteModuleConfigByClassName;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

remoteModuleConfigByClassName = [[NSMutableDictionary alloc] init];
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {

NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
RCTSparseArray *exportedMethodsByModuleID = RCTExportedMethodsByModuleID(moduleLoader);
[moduleLoader.moduleClassesByModuleID enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
NSArray *methods = exportedMethodsByModuleID[moduleID];
NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity:methods.count];
[methods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *_stop) {
methodsByName[method.JSMethodName] = @{
Expand Down Expand Up @@ -503,6 +464,7 @@ - (NSString *)description

@implementation RCTBridge
{
RCTBridgeModuleLoader *_moduleLoader;
RCTSparseArray *_modulesByID;
NSDictionary *_modulesByName;
id<RCTJavaScriptExecutor> _javaScriptExecutor;
Expand All @@ -523,6 +485,7 @@ - (instancetype)initWithBundlePath:(NSString *)bundlePath
_bundlePath = bundlePath;
_moduleProvider = block;
_launchOptions = launchOptions;
_moduleLoader = [RCTBridgeModuleLoader sharedLoader];
[self setUp];
[self bindKeys];
}
Expand All @@ -541,35 +504,27 @@ - (void)setUp
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);

// Register passed-in module instances
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) {
preregisteredModules[RCTModuleNameForClass([module class])] = module;
}

// Instantiate modules
_modulesByID = [[RCTSparseArray alloc] init];
NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy];
[RCTBridgeModuleClassesByModuleID() enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
NSString *moduleName = RCTModuleNamesByID[moduleID];
// Check if module instance has already been registered for this name
if ((_modulesByID[moduleID] = modulesByName[moduleName])) {
// Preregistered instances takes precedence, no questions asked
if (!preregisteredModules[moduleName]) {
// It's OK to have a name collision as long as the second instance is nil
RCTAssert([[moduleClass alloc] init] == nil,
@"Attempted to register RCTBridgeModule class %@ for the name '%@', \
but name was already registered by class %@", moduleClass,
moduleName, [modulesByName[moduleName] class]);
}
} else {
// Module name hasn't been used before, so go ahead and instantiate
NSMutableDictionary *modulesByName = [NSMutableDictionary dictionary];
if (_moduleProvider) {
// Register passed-in module instances
for (id<RCTBridgeModule> module in _moduleProvider()) {
NSUInteger moduleID = [_moduleLoader addModuleClass:[module class]];
NSString *moduleName = [_moduleLoader moduleNameForModuleID:moduleID];
_modulesByID[moduleID] = module;
modulesByName[moduleName] = module;
}
} else {
// Automatically load and instantiate all modules (note: this can take several hundred milliseconds)
[_moduleLoader loadAllModuleClasses];
[_moduleLoader.moduleClassesByModuleID enumerateObjectsUsingBlock:^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
NSString *moduleName = [_moduleLoader moduleNameForModuleID:moduleID];
id<RCTBridgeModule> module = [[moduleClass alloc] init];
if (module) {
_modulesByID[moduleID] = modulesByName[moduleName] = module;
}
}
}];
}];
}

// Store modules
_modulesByName = [modulesByName copy];
Expand All @@ -583,9 +538,9 @@ - (void)setUp

// Inject module data into JS context
NSString *configJSON = RCTJSONStringify(@{
@"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName),
@"localModulesConfig": RCTLocalModulesConfig()
}, NULL);
@"remoteModuleConfig": RCTRemoteModulesConfig([RCTBridgeModuleLoader sharedLoader], _modulesByName),
@"localModulesConfig": RCTLocalModulesConfig()
}, NULL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
dispatch_semaphore_signal(semaphore);
Expand Down
20 changes: 20 additions & 0 deletions React/Base/RCTBridgeModuleConfiguration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

@import Foundation;

@class RCTBridgeModuleLoader;

@interface RCTBridgeModuleConfiguration : NSObject

- (instancetype)initFromModuleLoader:(RCTBridgeModuleLoader *)moduleLoader;



@end
14 changes: 14 additions & 0 deletions React/Base/RCTBridgeModuleConfiguration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "RCTBridgeModuleConfiguration.h"

@implementation RCTBridgeModuleConfiguration

@end
24 changes: 24 additions & 0 deletions React/Base/RCTBridgeModuleLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

@import Foundation;

@interface RCTBridgeModuleLoader : NSObject

@property (nonatomic, strong, readonly) NSArray *moduleClassesByModuleID;

+ (instancetype)sharedLoader;

- (NSUInteger)addModuleClass:(Class)moduleClass;
- (void)loadAllModuleClasses;

- (NSUInteger)moduleIDForModuleName:(NSString *)moduleName;
- (NSString *)moduleNameForModuleID:(NSUInteger)moduleID;

@end
80 changes: 80 additions & 0 deletions React/Base/RCTBridgeModuleLoader.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "RCTBridgeModuleLoader.h"

#import "RCTBridgeModule.h"
#import "RCTUtils.h"

@implementation RCTBridgeModuleLoader {
NSMutableArray *_moduleNamesByID;
NSMutableArray *_moduleClassesByID;
NSMutableDictionary *_moduleIDsByName;
}

+ (instancetype)sharedLoader
{
static RCTBridgeModuleLoader *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[RCTBridgeModuleLoader alloc] init];
});
return instance;
}

- (instancetype)init
{
if (self = [super init]) {
_moduleNamesByID = [NSMutableArray array];
_moduleClassesByID = [NSMutableArray array];
_moduleIDsByName = [NSMutableDictionary dictionary];
}
return self;
}

- (NSArray *)moduleClassesByModuleID
{
return _moduleClassesByID;
}

- (NSUInteger)addModuleClass:(Class)moduleClass
{
NSString *moduleName = RCTModuleNameForClass(moduleClass);
NSNumber *moduleIDNumber = _moduleIDsByName[moduleName];
if (moduleIDNumber) {
return moduleIDNumber.unsignedIntegerValue;
}

NSUInteger moduleID = _moduleNamesByID.count;
_moduleIDsByName[moduleName] = @(moduleID);
[_moduleNamesByID addObject:moduleName];
[_moduleClassesByID addObject:moduleClass];
return moduleID;
}

- (void)loadAllModuleClasses
{
RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
if ([cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
[self addModuleClass:cls];
}
});
}

- (NSUInteger)moduleIDForModuleName:(NSString *)moduleName
{
return ((NSNumber *)_moduleIDsByName[moduleName]).unsignedIntegerValue;
}

- (NSString *)moduleNameForModuleID:(NSUInteger)moduleID
{
return _moduleNamesByID[moduleID];
}

@end
3 changes: 3 additions & 0 deletions React/Base/RCTUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector);
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);

// Returns the canonical JS module name for the given bridge module class
NSString *RCTModuleNameForClass(Class cls);

// Enumerate all classes that conform to NSObject protocol
void RCTEnumerateClasses(void (^block)(Class cls));

Expand Down
6 changes: 6 additions & 0 deletions React/Base/RCTUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#import <CommonCrypto/CommonCrypto.h>

#import "RCTBridgeModule.h"
#import "RCTLog.h"

NSString *RCTJSONStringify(id jsonObject, NSError **error)
Expand Down Expand Up @@ -183,6 +184,11 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
return NO;
}

NSString *RCTModuleNameForClass(Class cls)
{
return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
}

void RCTEnumerateClasses(void (^block)(Class cls))
{
static Class *classes;
Expand Down
Loading

0 comments on commit b8271e9

Please sign in to comment.