You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RCTBridgeModuleClasses currently scans all(!) of the classes available at runtime to find which are modules. I believe a more explicit approach where modules are loaded only when needed is better for a couple of reasons:
No need to loop through all iOS classes
Fewer side effects from implicitly invoking +[load] and +[initialize] on otherwise-unused classes
Modules can be loaded on the JavaScript thread instead of the main thread -- some of the aforementioned side-effect methods in unused classes expect to run on the main thread
Our initial implementation is just going to replace the class crawling with an alternative automatic registration mechanism that uses mach-O data headers (much the same as how methods are currently registered with RCT_EXPORT).
This means we get to keep the automatic module registration, but get rid of the need to scan every class in the whole app. Automatic registration has a lot of advantages, and you can already opt-out of it if necessary by returning nil from init (or, in the new system, by not adding the RCT_EXPORT_MODULE macro).
Lazy init is something I've been thinking about, but there are certain cases where you want your module to be initialized immediately - for example if it broadcasts notifications, or needs to register some constants - so it's not simply a case of waiting until the first time it's called.
One goal of mine to be able to disable automatic module registration so that I can provide different module implementations (or exclude modules) for different root views, so I'm looking forward to the new system.
I had a module loader that handled the module class <-> ID <-> name mapping and could generate the "remote module" and "local module" configs. This let me delete most of the static functions and variables in RCTBridge.m, which I wanted so that two different root views could use two separate sets of remote & local modules.
Re: lazy init -- currently I'm thinking that the number of modules should be fairly small (under 1000) it actually seems fine to initialize all modules up front and the ones that do heavy work can implement some kind of lazy initialization on their own.
As you say, there shouldn't be much overhead to having extra modules initialised that you aren't using, so our current recommendation if you want different modules in two different root views is simply to load all of them in both - especially since we've now set it up so root views can share the same bridge (which is a much bigger resource saving, as it means there's only only JavaScript context running).
It's also possible to override built-in modules with your own by injecting another module with the same moduleName, so I think we've covered cases like test mocking, or providing your own versions of (for example) XHP, that use your existing network stack (we do this for our internal apps).
Please do let us know if you have use cases we aren't handling yet though - and if you want to share your alternative architecture idea as an "RFC" pull request, then please do that too - we might still incorporate some of the ideas :-)
One feature I'd like is the ability to enable or disable autoloading for specific bridges. For example one bridge may autoload all linked modules while another bridge would only have modules provided to it. I could work on a PR when you guys complete the new module loader but it would be great if the code you're currently writing anticipated this feature or even implemented it.
Activity
ide commentedon Apr 6, 2015
Working on this now. I expect a good performance win and a more encapsulated API (replacing a lot of globals) to come out of this.
sophiebits commentedon Apr 6, 2015
I think @nicklockwood and @a2 are planning to get this in tomorrow actually.
sophiebits commentedon Apr 6, 2015
Or at least, some version of it that removes the class-crawling.
nicklockwood commentedon Apr 6, 2015
We are. I'm interested to hear your thoughts on how to approach it though.
nicklockwood commentedon Apr 6, 2015
Our initial implementation is just going to replace the class crawling with an alternative automatic registration mechanism that uses mach-O data headers (much the same as how methods are currently registered with RCT_EXPORT).
This means we get to keep the automatic module registration, but get rid of the need to scan every class in the whole app. Automatic registration has a lot of advantages, and you can already opt-out of it if necessary by returning nil from init (or, in the new system, by not adding the RCT_EXPORT_MODULE macro).
Lazy init is something I've been thinking about, but there are certain cases where you want your module to be initialized immediately - for example if it broadcasts notifications, or needs to register some constants - so it's not simply a case of waiting until the first time it's called.
ide commentedon Apr 7, 2015
OK. Thanks for letting me know.
One goal of mine to be able to disable automatic module registration so that I can provide different module implementations (or exclude modules) for different root views, so I'm looking forward to the new system.
I had a module loader that handled the module class <-> ID <-> name mapping and could generate the "remote module" and "local module" configs. This let me delete most of the static functions and variables in RCTBridge.m, which I wanted so that two different root views could use two separate sets of remote & local modules.
Re: lazy init -- currently I'm thinking that the number of modules should be fairly small (under 1000) it actually seems fine to initialize all modules up front and the ones that do heavy work can implement some kind of lazy initialization on their own.
nicklockwood commentedon Apr 7, 2015
As you say, there shouldn't be much overhead to having extra modules initialised that you aren't using, so our current recommendation if you want different modules in two different root views is simply to load all of them in both - especially since we've now set it up so root views can share the same bridge (which is a much bigger resource saving, as it means there's only only JavaScript context running).
It's also possible to override built-in modules with your own by injecting another module with the same moduleName, so I think we've covered cases like test mocking, or providing your own versions of (for example) XHP, that use your existing network stack (we do this for our internal apps).
Please do let us know if you have use cases we aren't handling yet though - and if you want to share your alternative architecture idea as an "RFC" pull request, then please do that too - we might still incorporate some of the ideas :-)
ide commentedon Apr 7, 2015
One feature I'd like is the ability to enable or disable autoloading for specific bridges. For example one bridge may autoload all linked modules while another bridge would only have modules provided to it. I could work on a PR when you guys complete the new module loader but it would be great if the code you're currently writing anticipated this feature or even implemented it.
I had a diff about 1/3 complete where the only moderately interesting part is being able to skip autoloading if the module provider is specified: ide@b8271e9#diff-e15318f48b6447f2d9936c5e047d882fR509
ide commentedon May 29, 2015
The new module system from awhile back addressed most of what this issue was about. Closing out.
Merge pull request facebook#69 from melpomene/master