React native is Facebook just open source framework, you can use JavaScript directly to develop native apps, do not say that the framework follow-up can be recognized by the public, single from the source, the framework of the source has a lot of design ideas and implementation of the way is worth learning, This article first looks at its most basic JAVASCRIPT-OBJECTC communication mechanism (hereinafter referred to as JS/OC).
Overview
React native with iOS comes with JavaScriptCore as the parsing engine of JS, but did not use the JavaScriptCore to provide some of the features can make JS and OC Intermodulation, but to implement a set of mechanisms, this mechanism can be common to all JS engine, In the absence of JavaScriptCore can also be replaced with webview, in fact, the project already has the use of WebView as the resolution engine implementation, should be used for compatibility with iOS7 the following no JavaScriptCore version.
Ordinary Js-oc communication is actually very simple, OC to JS to transmit information has a ready interface, like WebView provides-stringbyevaluatingjavascriptfromstring method can directly in the current context to execute a JS script, And can get the return value after execution, this return value is equivalent to JS to the OC to pass information. React native is also based on this, through a variety of means, to achieve the definition of a module in OC method, JS can directly call this module method and can also seamlessly join the callback.
For example, OC defines a module Rctsqlmanager, which has a method-query:successcallback:,js can call rctsqlmanager.query directly and get execution results through callbacks. :
|
//oc:@implementrctsqlmanager-(void) query: (nsstring*) querydata Successcallback: (rctresponsesenderblock) responsesender{     rct _export ();     nsstring*ret =@ "ret"     responsesender (ret);}@end |
|
//JS:RCTSQLManager.query("SELECT * FROM table",function(result) { //result == "ret";}); |
Let's see how it's implemented.
Module Configuration Table
First OC to tell JS what module it has, what method in the module, JS only know that there are these methods can be called after these methods. The implementation here is OC generates a Module configuration table to JS, the configuration list includes all modules and modules in the method information. Cases:
|
{ "remoteModuleConfig": { "RCTSQLManager": { "methods": { "query": { "type":"remote", "methodID": 0 } }, "moduleID": 4 }, ... },} |
OC End and JS end each have a bridge, two bridge has saved the same Module configuration table, JS call OC Module method, through the configuration table in bridge module method to module ID and method ID passed to OC, OC finds the corresponding method execution through the module configuration table of Bridge, taking the above code as an example, the process is like this (regardless of callback):
Before we get to the call flow, let's take a look at the Module configuration table for OC. When we create a new OC module, JS and OC do not need to manually go to the new module to add some configuration, the module configuration table is automatically generated, as long as there is a module in the project, the module will be added to the configuration table, how this module configuration table is automatically generated? In two steps:
1. Take all module classes
Each module class implements the Rctbridgemodule interface, which allows all classes in the project to be fetched through the runtime interface Objc_getclasslist or Objc_copyclasslist. Then one by one to determine whether the implementation of the Rctbridgemodule interface, you can find all the module classes, implemented in the Rctbridgemoduleclassesbymoduleid () method.
2. Method of exposing JS to module
A module can have a lot of methods, some can be exposed to JS direct call, some private do not want to expose to JS, how to extract these exposure method? The way I can think of is to make some rules for the name of the method to be exposed, such as using Rctexport_ as a prefix, and then using the runtime method to class_getinstancemethod out all the method names, extracting the Rctexport_ prefix method, But the disgusting thing about doing this is that each method must be prefixed. React native used another dark magic method to solve the problem: compiling the property __attribute__.
In the above example, we see the module method in the code: Rct_export (), the method in the module plus this macro can be implemented to expose to JS, no other rules, what is this macro do? Let's take a look at its definition:
|
#define RCT_EXPORT(JS_name) __attribute__((used, section("__DATA,RCTExport" \)))staticconstchar*__rct_export_entry__[] = { __func__, #JS_name } |
The function of this macro is to create a new section of the binary file with the compiler attribute __attribute__, which belongs to the __data data segment, named Rctexport, and adds the current method name to this paragraph. At compile time, the compiler will find __attribute__ to process and add the appropriate content to the resulting executable file. The effect can be seen from Linkmap:
|
# Sections:# Address Size Segment Section0x100001670 0x000C0180 __TEXT __text...0x10011EFA0 0x00000330 __DATA RCTExport0x10011F2D0 0x00000010 __DATA __common0x10011F2E0 0x000003B8 __DATA __bss...0x10011EFA0 0x00000010 [ 4] -[RCTStatusBarManager setStyle:animated:].__rct_export_entry__0x10011EFB0 0x00000010 [ 4] -[RCTStatusBarManager setHidden:withAnimation:].__rct_export_entry__0x10011EFC0 0x00000010 [ 5] -[RCTSourceCode getScriptText:failureCallback:].__rct_export_entry__0x10011EFD0 0x00000010 [ 7] -[RCTAlertManager alertWithArgs:callback:].__rct_export_entry__... |
Can see the executable file data segment more than a rctexport segment, the content is to be exposed to the JS method. These contents can be obtained at runtime, obtained in RCTBRIDGE.M's Rctexportedmethodsbymoduleid () method, extract the class name and method name of each method, and complete the work of exposing the JS method in the extraction module.
The overall module class/method extraction is implemented in the Rctremotemodulesconfig () method.
Invoke process
Next look at the detailed process of JS calling the OC module method, including the callback callback. At this point you need to refine the above call flowchart:
Seems a little complicated, but step by step, it should be easy to figure out the whole process, each process in the diagram is marked with a sequence number, from the initiating call to the execution of the callback total of 11 steps, detailed the following steps:
The 1.JS end invokes the method exposed by one of the OC modules.
2. Break the call of the previous step into modulename,methodname,arguments, then throw it to the MessageQueue handle.
In the Module configuration table on the initialization of each module has generated a corresponding Remotemodule object, the object is also generated with the module configuration table one by one corresponding method, these methods can get their own module name, method name, and callback to do some processing, and handed over to MessageQueue. Concrete implementation in the Batchedbridgefactory.js _createbridgedmodule, the entire implementation of a mere 24 lines of code, feel the magic of JS.
3. In this step, JS's callback function is cached in a member variable of MessageQueue, using Callbackid to represent callback. In the Module configuration table saved in MessageQueue the previous step passed in ModuleName and MethodName to ModuleID and Methodid.
4. Pass the Moduleid,methodid,callbackid and other parameters obtained from the above steps Argus to OC. As for the specific how to preach, and later.
5.OC received the message, through the module configuration table to get the corresponding modules and methods.
In fact, the module configuration table has been processed, like JS, during initialization, OC also generated a corresponding instance of each module on the module configuration table and cached, each method on the module also generated a corresponding Rctmodulemethod object, This is called by ModuleID and Methodid to the corresponding module instance and Rctmodulemethod instance. Specifically implemented in _handlerequestnumber:moduleid:methodid:params:.
6.RCTModuleMethod to the JS pass each parameter to deal with.
Rctmodulemethod can get OC to invoke the target method of each parameter type, processing JS type to the target type of conversion, all JS Pass the number is NSNumber, here will be converted to the corresponding int/long/double and other types, More importantly, a block is generated for the block type parameter.
For example-(void) Select: (int) index response: (Rctresponsesenderblock) Callback This method, get two parameters of type Int,block, JS pass over the two parameter type is nsnumber,nsstring (callbackid), this time will convert NSNumber to int,nsstring (Callbackid) into a block, The content of block is to pass back the value of callback and Callbackid to JS.
After these parameters are assembled, the corresponding OC module method is called dynamically via nsinvocation.
After the 7.OC module method is called, the block callback is executed.
8. Call the block generated by the Rctmodulemethod to the 6th step description.
9.block with Callbackid and block passed over parameters to tune JS MessageQueue method Invokecallbackandreturnflushedqueue.
10.MessageQueue through Callbackid find the corresponding JS callback method.
11. Call the callback method and pass the parameters that OC brings over to complete the callback.
The whole process is like this, simple generalization, almost is: JS function call Moduleid/methodid, callback to Callbackid, OC based on ID get method, processing parameters, called OC method, Callback Callbackid-JS through Callbackid to get callback execution
Incident response
The 4th step left a question, JS is how to pass data to OC, let OC to adjust the corresponding method?
The answer is through the return value. JS will not actively transmit data to OC, in the OC method, the 4th step in the above Moduleid,methodid and other data added to a queue, and so OC came to tune the arbitrary method JS, then the queue back to OC, at this time OC then execute the queue to invoke the method.
At first do not understand, designed to JS can not directly call OC, need to be in OC to tune JS when the return value triggered by the call, the whole program can run through it. Then think of the pure native development of the incident response mechanism, a little understanding. When will the code be executed in native development? This event can be a startup event, a touch event, a timer event, a system event, and a callback event only when an event is triggered. And in react native, these events occur when OC will call JS corresponding module method to handle, after processing these events and then execute JS to let OC execute the method, and no event occurs, will not execute any code, which is consistent with the native development event response mechanism.
Speaking of OC Call JS, and then add, in fact, the module configuration table in addition to the above OC module Remotemodules, but also save the JS module Localmodules,oc JS Some module method, but also by passing ModuleID and Methodid to call the , will go to-enqueuejscall:args: method to pass two ID and parameters to JS Batchedbridge.callfunctionreturnflushedqueue, and JS tune oc principle is similar, no longer repeat.
Summarize
The JS-OC communication mechanism of the whole react native is basically this, the key point is: modularization, module configuration table, pass ID, encapsulation call, event response, its design ideas and implementation method is worth learning from
React native communication mechanism detailed