Weex is how to run on the iOS client

Weex is how to run on the iOS client

Preface

April 21, 2016, Alibaba announced at the Qcon conference cross platform mobile development tool Weex open beta invitation. Weex able to take into account the performance and dynamic performance, so that mobile developers to write a Native level performance experience through a simple front-end syntax, and support iOS, Android, YunOS and Web, such as multi deployment.

Over the past year, ReactNative and Weex these cross platform technology for Native developers, the impact is huge. Native has some disadvantages in the development of App, such as the client require frequent updates, contain iOS update time is also subject to audit; iOS, Android and front end at the same time the development of the same demand in personnel costs on consumption; the performance of Hybrid and Native compared to a little poor.

The emergence of ReactNative and Weex is to solve these pain points.

Weex is how to run on the iOS client

Announced in April 21st after the beta, just two weeks, there are more than 5000 developers to apply.

Weex is how to run on the iOS client

June 30, 2016 Alibaba officially announced Weex open source. As with Web, billion level application level performance Native development experience ingenuity to create cross platform mobile Weex development tools in the open on the first day aboard the Github trend charts, so far, the number of Weex Star in GitHub has reached 13393. One of the most popular open source projects on Github in China in 2016.

Catalog

  • 1.Weex overview
  • 2.Weex working principle
  • 3.Weex how to run on iOS
  • 4 about Weex, ReactNative, JSPatch

I. Weex overview

Weex from the day of birth, as if and ReactNative is a pair”.

Weex is how to run on the iOS client

ReactNative claims “Learn once, write anywhere”, while Weex claims “Write Once, Run Everywhere”. Weex from the day of birth, it is given the unification of three end hopes. ReactNative can support iOS, Android, and Weex can support iOS, Android, HTML5. The unification of three terminal can solve second pain points in which said, while the development of waste personnel cost problem.

Weex is how to run on the iOS client

Native mobile developers only need to import the local Weex SDK, you can use the HTML/CSS/JavaScript page of this programming language to develop Native level Weex interface. This means that can be directly used for completion, existing Web development editor and IDE code tips, check function. So as to the front end of the development of Native, lower development costs and learning costs.

Weex is how to run on the iOS client

Weex is a lightweight, scalable, high-performance framework. Integration is also very convenient, can be embedded directly in the HTML5 page, can also be embedded in the native UI. Because, like ReactNative, will call the Native side of the native controls, so the performance of a higher level than Hybrid. This solves the third pain points mentioned in the preface.

Weex is how to run on the iOS client

Weex very lightweight, compact, simple syntax, easy access and use. ReactNative official only allows the ReactNative base JS library and business JS together into a JS bundle, does not provide the functionality of the sub package, so if you want to save traffic must be made subcontracting tool. While the Weex default hit JS bundle contains only a small number of JS business code, based JS library contained in Weex SDK, compared with the Weex and Facebook React Native and Microsoft Cordova, Weex is more lightweight, small size. The Weex generated JS bundle easily deployed to the server, and then Push to the client, or the client request new resources to complete the release. Such a fast iterative solution to the introduction of the first point of pain that can not control the release time,

Weex is how to run on the iOS client

Weex Native components and API can be extended horizontally, the business can go to the center of the horizontal activation of customized components and functional modules. And can also be reused directly Web front-end engineering management and monitoring tools.

Know almost have a good contrast on the Weex and ReactNative article weex& ReactNative contrast, recommend reading.

Weex officially released v0.10.0 in February 17, 2017, this milestone version began to be perfectly compatible with the Vue.js development Weex interface.

Weex also moved to the Apache foundation in February 2017 24, Alibaba will continue to iterate based on Apache infrastructure. And enabled a new GitHub warehouse: https://github.com/apache/incubator-weex

The following source analysis is based on the v0.10.0 version.

Two. Weex working principle

Weex is how to run on the iOS client

The picture above is an official to a schematic, Weex is how to package the JS JS Bundle principle of this article does not involve. This article will analyze in detail how Weex is working on the Native side. The author of the Native end of the principle of subdivision, as follows:

Weex is how to run on the iOS client

Weex can design their own DSL, writing.We files or.Vue files to develop the interface, the entire page is divided into 3 sections, template, style, script, drawing on the idea of mature MVVM.

Weex in terms of performance, in order to improve the performance of possible client, DSL Transformer all on the server, Weex server in the XML + CSS + JavaScript code are converted to JS Bundle. Server deploys JS Bundle to Server and CDN.

Weex and React Native, Weex JS Framework built in SDK, used to parse the downloaded from the server on the JS Bundle, this also reduces the volume of each JS Bundle, there is no need to sub problem React Native. After the client request JS Bundle to JS Framework, JS Framework analysis after the completion of the Json format output will Virtual DOM client Native only need to parse and render the layout, UI Virtual DOM was responsible for. However, this set of analysis, layout, rendering the basic realization of the logic SDK.

Finally, the three sides of the Weex support agreement, a JS server on the Bundle, through analysis, to achieve the consistency of the iOS/Android/HTML5 three.

Three. Weex how to run on iOS

Through the analysis of the previous chapter, we know the whole process of Weex, because of the lack of knowledge from the front, so this paper analysis.We or.Vue source files to JS bundle in front of this part of the temporary does not involve, after the familiar front, this will fill up.

Before the analysis of the first point, Weex all the source code has actually been open source, as for the SDK Demo which also relies on a ATSDK.framework, this is not open source. ATSDK.framework this is actually a plug-in Weex performance monitoring.

Weex is how to run on the iOS client

It’s the gray box in the picture above. Some of the plug-ins have their own APM, Ali is not open source this, but all the features of the Weex is not affected.

Then the next detailed analysis in the iOS Native side, Weex is how to run up. Direct source analysis.

(a) Weex SDK initialization

This is the end of the Native want to run the first step Weex.

- (BOOL) application: (UIApplication * application) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions [[UIWindow alloc] initWithFrame:[UIScreen {self.window = mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor] SDK [self initWeexSDK]; / / initialize here; self.window.rootViewController = [[WXRootViewController alloc] initWithRootViewController:[self demoController]]; [self.window makeKeyAndVisible]; return YES;}

In the application: didFinishLaunchingWithOptions: function to initialize SDK. There are a lot of things to initialize. Some people may ask, initialization is written here, but also initialize so many things, will not start the card App time? Go ahead with this question.

#pragma mark weex (void) initWeexSDK {[WXAppConfiguration setAppGroup:@ "AliApp"]; [WXAppConfiguration setAppName:@ "WeexDemo"]; [WXAppConfiguration setExternalUserAgent:@ "ExternalUA"]; [WXSDKEngine initSDKEnvironment]; WXSDKEngine registerHandler:[WXImgLoaderDefaultImpl new] [withProtocol:@protocol] (WXImgLoaderProtocol); [WXSDKEngine registerHandler:[WXEventModule new] withProtocol:@protocol (WXEventModuleProtocol)]; [WXSDKEngine registerComponent:@ "select" withClass:NSClassFromString (@ WXSelectComponent) [WXSDKEngine; registerModule:@ "event" withClass:[WXEventModule [WXSDKEngine registerModule:@ "syncTest" class]]; withClass:[WXSyncTestModule class]]; #if (TARGET_IPHONE_SIMULATOR) [self checkU! Pdate]; #endif #ifdef DEBUG [self atAddPlugin]; [WXDebugTool setDebug:YES]; [WXLog setLogLevel:WXLogLevelLog]; #ifndef UITEST [[ATManager shareInstance] show]; #endif #else [WXDebugTool setDebug:NO]; [WXLog setLogLevel:WXLogLevelError]; ENDIF #}

This is the original content to be initialized in the application: didFinishLaunchingWithOptions. We read the lines one by one.

WXAppConfiguration is a single object used to record App configuration information.

@interface WXAppConfiguration: NSObject @property (nonatomic, strong) NSString * appGroup; @property (nonatomic, strong) NSString * appName; @property (nonatomic, strong) NSString * appVersion; @property (nonatomic, strong) NSString * externalUA; @property (nonatomic, strong) NSString * JSFrameworkVersion; @property (nonatomic, strong) NSArray * customizeProtocolClasses; / * * * AppGroup name or company name, the default value is nil * + (NSString *) appGroup; (void) + setAppGroup: (NSString * appGroup); / * * * app name, the default value is main bundle CFBundleDisplayName * + (NSString *) appName; (void) + setAppName: (NSString *) appName; / * * * app version information, the default value is main bundle CFBundleShortVersionString (NS + * / String * + appVersion); (void) setAppVersion: (NSString * appVersion); / * * * app outside the user agent name, request header all Weex will set up a user agent user agent field, the default value is nil * + (NSString *) externalUserAgent; (void) + setExternalUserAgent: (NSString * userAgent); / * * * the version of JSFrameworkVersion * + (NSString *) JSFrameworkVersion; (void) + setJSFrameworkVersion: (NSString * JSFrameworkVersion); / * * * / custom customizeProtocolClasses + (NSArray*) customizeProtocolClasses; (void) + setCustomizeProtocolClasses: (NSArray*) customizeProtocolClasses; @end

Note that all of the WXAppConfiguration method is a plus class method, the internal implementation is implemented with a single example of WXAppConfiguration, where the class method is to facilitate our call.

Next is the actual code to initialize the SDK.

[WXSDKEngine initSDKEnvironment];

On the specific implementation of initialization, see below, which marked the notes:

(void + initSDKEnvironment) {/ / dot recording state WX_MONITOR_PERF_START (WXPTInitalize) WX_MONITOR_PERF_START (WXPTInitalizeSync) / / main.js NSString loading local *filePath = [[NSBundle bundleForClass:self] pathForResource:@ "main" ofType:@ "JS"]; NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] SDK [WXSDKEngine initSDKEnvironment:script]; / / initialize environment; / / dot recording state WX_MONITOR_PERF_END (WXPTInitalizeSync) / / simulator #if TARGET_OS_SIMULATOR static dispatch_once_t version of the special code onceToken; dispatch_once (& onceToken, ^{[WXSimulatorShortcutManager registerSimulatorShortcutWithKey:@ "I" modifierFlags:UIKeyMo DifierCommand UIKeyModifierAlternate action:^{NSURL *URL | [NSURL URLWithString:@ = "http://localhost:8687/launchDebugger"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler: (NSData *data, NSURLResponse ^ *response, NSError *error) {/ /...}]; [task resume]; WXLogInfo (@ "Launching browser..." (dispatch_time dispatch_after); (DISPATC H_TIME_NOW, (int64_t) (1 * NSEC_PER_SEC)), dispatch_get_main_queue ([self), ^{connectDebugServer:@ "ws://localhost:8687/debugger/0/renderer"]);};}}); #endif}];

Here the SDKEnvironment initialization is divided into four steps, the WXMonitor monitor records, local loading main.js, WXSDKEngine initialization, WXSimulatorShortcutManager connect to the local server simulator. Next step by step analysis.

1 WXMonitor monitor record status

Weex is how to run on the iOS client

WXMonitor is a common object, which only stores a thread safe dictionary WXThreadSafeMutableDictionary.

@interface WXThreadSafeMutableDictionary< KeyType, ObjectType> NSMutableDictionary: @property (nonatomic, strong) dispatch_queue_t queue @property (nonatomic, strong); NSMutableDictionary* dict; @end

Initializes a queue when the dictionary is initialized.

- (instancetype) init initCommon] if {self = [self; _dict = [NSMutableDictionary (self) {dictionary]}; return self;} - (instancetype initCommon) {self = [super init]; if (self) {NSString* UUID = [NSString stringWithFormat:@ com.taobao.weex.dictionary_%p, self]; _queue = dispatch_queue_create ([uuid UTF8String], DISPATCH_QUEUE_CONCURRENT);} return self;}

Each time a WXThreadSafeMutableDictionary is generated, there will be a memory address corresponding to the corresponding Concurrent queue.

This queue ensures thread security.

- (NSUInteger) count NSUInteger count {__block; dispatch_sync (_queue, ^{count = _dict.count;}); return count;} - (ID) objectForKey: (ID) aKey ID obj {__block; dispatch_sync (_queue, ^{obj = _dict[aKey];}); return obj;} - {__block (NSEnumerator * keyEnumerator) NSEnumerator *enu; dispatch_sync (_queue, ENU ^{= [_dict keyEnumerator]; return ENU;};}) - (ID) copy{__block ID copyInstance; dispatch_sync (_queue, copyInstance ^{= [_dict copy]; return copyInstance;}});

Count, objectForKey:, keyEnumerator, copy these four operations are synchronous operation, using dispatch_sync to protect the thread safety.

- (void) setObject: (ID) anObject forKey: (id< NSCopying> aKey) {aKey = [aKey copyWithZone:NULL]; dispatch_barrier_async (_queue, ^{_dict[aKey] = anObject;});} - (void) removeObjectForKey: (ID) aKey (_queue, [_dict ^{{dispatch_barrier_async removeObjectForKey:aKey];});} - (void) removeAllObjects{dispatch_barrier_async (_queue, ^{[_dict removeAllObjects];}});

SetObject:forKey:, removeObjectForKey:, removeAllObjects these three operations with the dispatch_barrier_async.

The role of WXMonitor in the entire Weex is to record the tag value of each operation and the reasons for the success and failure of the record. WXMonitor encapsulates a variety of macros to facilitate the invocation of methods.

#define WX_MONITOR_PERF_START (tag) [WXMonitor performancePoint:tag willStartWithInstance:nil]; #define WX_MONITOR_PERF_END (tag) [WXMonitor performancePoint:tag didEndWithInstance:nil]; #define WX_MONITOR_INSTANCE_PERF_START (tag, instance) [WXMonitor performancePoint:tag willStartWithInstance:instance]; #define WX_MONITOR_INSTANCE_PERF_END (tag, instance) [WXMonitor performancePoint:tag didEndWithInstance:instance]; #define WX_MONITOR_PERF_SET (tag, value, instance) [WXMonitor performancePoint:tag didSetValue:value withInstance:instance]; #define WX_MONITOR_INSTANCE_PERF_IS_RECORDED (tag, instance) [WXMonitor performancePoint:tag isRecordedWithInstance:instance] / / these macros will correspond to these specific implementation methods. (void) + performancePoint: (WXPerformanceTag) tag willStartWithInstance: (WXSDKInstance * instance); (void) + performancePoint: (WXPerformanceTag) tag didEndWithInstance: (WXSDKInstance * instance); (void) + performancePoint: (WXPerformanceTag) tag didSetValue: (double) value withInstance: (WXSDKInstance * instance); (BOOL) + performancePoint: (WXPerformanceTag) tag (isRecordedWithInstance: WXSDKInstance * instance);

The entire operation is defined as 2 categories, one is the global operation, one is the specific operation.

Typedef enum: NSUInteger {global / WXPTInitalize = 0, WXPTInitalizeSync, WXPTFrameworkExecute, WXPTJSCreateInstance, instance / WXPTJSDownload, WXPTFirstScreenRender, WXPTAllRender, WXPTBundleSize, WXPTEnd} WXPerformanceTag;

Prior to WXSDKInstance initialization, all global global operations will be placed in the WXMonitor WXThreadSafeMutableDictionary. When the WXSDKInstance is initialized, that is, all operations in the WXPerformanceTag below instance will be placed in the WXSDKInstance performanceDict, note that performanceDict is not thread safe.

For instanceļ¼š

(void) + performancePoint: (WXPerformanceTag) tag willStartWithInstance: (WXSDKInstance * instance) {NSMutableDictionary *performanceDict = [self performanceDictForInstance:instance]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:2]; dict[kStartKey] = @ (CACurrentMediaTime) (* 1000); performanceDict[@ (tag)] = dict;}

All operations will be recorded in time:

WX_MONITOR_PERF_START (WXPTInitalize) WX_MONITOR_PERF_START (WXPTInitalizeSync)

WXThreadSafeMutableDictionary dictionary which will store similar data:

{0 = {start = "";}; = = start = "146578019.356428";};}

The dictionary will be based on the operation of the tag as a key value. WX_MONITOR_PERF_START and WX_MONITOR_PERF_END are generally appear in pairs, after the end of the initialization will call WX_MONITOR_PERF_END. The dictionary will be saved in the following:

{0 = {end = ""; start = "148484241.723654";}; = = end = ""; start = "148485865.699819";};}

WXMonitor will also record some success and failure information:

#define WX_MONITOR_SUCCESS_ON_PAGE (tag, pageName) [WXMonitor monitoringPointDidSuccess:tag onPage:pageName]; #define WX_MONITOR_FAIL_ON_PAGE (tag, errorCode, errorMessage, pageName) / NSError *error = [NSError errorWithDomain: WX_ERROR_DOMAIN / code:errorCode / userInfo:@{NSLocalizedDescriptionKey: (errorMessage?: @ "No message")}] / [WXMonitor monitoringPoint:tag didFailWithError:error; onPage:pageName]; #define WX_MONITOR_SUCCESS (tag) WX_MONITOR_SUCCESS_ON_PAGE (tag, Nil) #define WX_MONITOR_FAIL (tag, errorCode, errorMessage) WX_MONITOR_FAIL_ON_PAGE (tag, errorCode, errorMessage, Nil) / / these macros will correspond to these specific implementation methods. (void) monitoringPointDidSuccess: (WXMonitorTag) tag onPage: (NSString *) pageName + (void) monitoringPoint: (WXMonitorTag) tag didFailWithError: (NSError *) error onPage: (NSString *) pageName;

These functions are not used here for the time being.

2 load local main.js

Weex is how to run on the iOS client

SDK inside will bring a main.js, open the file directly to see a bunch of files after webpack compression. The source file for this file is in the https://github.com/apache/incubator-weex/tree/master/html5 directory. The corresponding entry file is html5/render/native/index.js

Import from {subversion}'../../../package.json'import runtime from'../../runtime' import frameworks from'../../frameworks/index'import services from'../../services/index' const {init, config} = runtime config.frameworks = frameworks const {native}, transformer = Subversion (for const serviceName in services (serviceName, services[serviceName]) {runtime.service.register} (runtime.freezePrototype) (runtime.setNativeConsole) framework meta info global.frameworkVersion / / register) native = global.transformerVersion = transformer / init frameworks const globalMethods = init (config) global methods for (const / / set methodName in (globalMethods) {global[methodName] = => const... Args) {RET = globalMethods[methodN Ame] ((args) if (RET instanceof Error) {console.error (ret.toString)} return RET}}

This section of JS will be passed as a reference to WXSDKManager. It is also Native side JS framework.

3 WXSDKEngine initialization

WXSDKEngine initialization is the key to the entire SDK initialization.

(void) + initSDKEnvironment: (NSString * script) {if (script ||! Script.length < = 0) {WX_MONITOR_FAIL (WXMTJSFramework, WX_ERR_JSFRAMEWORK_LOAD, framework loading is @ failure! "); return;} / / Modules Handlers [self, registered Components, registerDefaults] JsFramework [[WXSDKManager bridgeMgr] executeJsFramework:script]; / / implementation;}

A total of two things to do, registered Components, Modules, Handlers and JSFramework.

First look at how to register.

(void + registerDefaults) {static dispatch_once_t onceToken; dispatch_once (& onceToken, [self _registerDefaultComponents] ^{; [self _registerDefaultModules]; [self _registerDefaultHandlers];}});

At the time of WXSDKEngine initialization, they registered the three things, Components, Modules, Handlers.

Weex is how to run on the iOS client

First look at Components:

(void + _registerDefaultComponents) {[self registerComponent:@ "container" withClass:NSClassFromString (@ WXDivComponent) withProperties:nil]; [self registerComponent:@ "div" withClass:NSClassFromString (@ WXComponent) withProperties:nil]; [self registerComponent:@ "text" withClass:NSClassFromString (@ WXTextComponent) withProperties:nil]; [self registerComponent:@ "image" withClass:NSClassFromString (@ WXImageComponent) withProperties:nil]; [self registerComponent:@ "scroller" (@ withClass:NSClassFromString "WXScrollerComponent" withProperties:nil] [self registerComponent:@ "); list withClass:NSClassFromString (@ WXListComponent) withProperties:nil]; [self registerComponent:@" header "withClass:NSClassFromString (@ WXHe AderComponent ")]; [self registerComponent:@" cell "withClass:NSClassFromString (@ WXCellComponent)]; [self registerComponent:@" embed "withClass:NSClassFromString (@ WXEmbedComponent)]; [self registerComponent:@" a "withClass:NSClassFromString (@ WXAComponent)]; [self registerComponent:@" select "withClass:NSClassFromString (@ WXSelectComponent)]; [self registerComponent:@ switch withClass:NSClassFromString (@ WXSwitchComponent)]; [self registerComponent:@" input "withClass:NSClassFromString (@ WXTextInputComponent)]; [self registerComponent:@" video "withClass:NSClassFromString (@ WXVideoComponent)]; [self registerComponent:@" indicator "withClass: NSClassFromString (@ WXIndicatorComponent)]; [self registerCo Mponent:@ "slider" withClass:NSClassFromString (@ WXSliderComponent)]; [self registerComponent:@ "Web" withClass:NSClassFromString (@ WXWebComponent)]; [self registerComponent:@ "loading" withClass:NSClassFromString (@ WXLoadingComponent)]; [self registerComponent:@ "loading-indicator" withClass:NSClassFromString (@ WXLoadingIndicator)]; [self registerComponent:@ "Refresh" withClass:NSClassFromString (@ "WXRefreshComponent)]; [self registerComponent:@" textarea "withClass:NSClassFromString (@ WXTextAreaComponent)]; [self registerComponent:@" canvas "withClass:NSClassFromString (@ WXCanvasComponent)]; [self registerComponent:@" slider-neighbor "withClass: NSClassFromString (@ WXSliderNeighborComponent)]};

In the WXSDKEngine initialization will default to register these 23 basic components. Here’s one of the most complex components, WXWebComponent, to see how it is registered.

The first thing you need to explain,

(void) + registerComponent: (NSString *) name withClass: (Class) clazz [self registerComponent:name withClass:clazz withProperties: @{@ {"append": @}] "tree";}

The difference between the registerComponent:withClass: method and the registerComponent:withClass:withProperties: method is the last one to the Senate is @{@ “append”: @ “tree”}, if it is marked as @ “tree”, then the syncQueue accumulation of a lot of tasks, will be forced to perform a layout.

So the above 23 basic components inside, only the first 5, container, div, text, image, scroller, list is not marked as @ tree, the remaining 18 are likely to enforce a layout.

(void) + registerComponent: (NSString *) name withClass: (Class) clazz withProperties: (NSDictionary * properties) {if (name ||!! clazz) {return} (name & WXAssert; & clazz, Fail to register the @ component, please check if the parameters are correct! "); / / 1.WXComponentFactory [WXComponentFactory registerComponent:name withClass:clazz withPros:properties] component registration; / / the 2 traversal methods all asynchronous NSMutableDictionary *dict = [WXComponentFactory componentMethodMapsWithName:name]; dict[@" type "] = name; / / registered 3 components to the WXBridgeManager if (properties) {NSMutableDictionary *props = [properties mutableCopy]; if ([dict[@ methods count]) {[props addEntriesFromDictionary:dict];}} else {[[WXSDKManager bridgeMgr] registerComponents:@[props]]; [[WXSDKManager bridgeMgr] registerComponents:@[dict]];}}

All registered components are registered by WXComponentFactory. WXComponentFactory is a single case.

@interface WXComponentFactory: NSObject {NSMutableDictionary *_componentConfigs; NSLock *_configLock;} @property (nonatomic, strong) NSDictionary *properties; @end

In WXComponentFactory, _componentConfigs will store all the component configuration, the registration process is also the process of generating _componentConfigs.

- (void) registerComponent: (NSString *) name withClass: (Class) clazz withPros: (NSDictionary * PROs) {WXAssert (name & & clazz, name or clazz must @ not be nil for registering component. "); WXComponentConfig *config = nil; [_configLock = lock]; config [_componentConfigs objectForKey:name]; / / if the component is already registered that will prompt repeated registration, and cover the original registration behavior if (config) {WXLogInfo (@ Overrider component name:%@ class:%@, to name:%@, config.name, class:% @ config.class, name, clazz);} config = [[WXComponentConfig alloc] initWithName:name class:NSStringFromClass (clazz) pros:pros]; [_componentConfigs setValue:config forKey:name]; / / registration method [config registerMethods]; [_configLock unlock];}

In the WXComponentFactory _componentConfigs dictionary will be in accordance with the name of the component as key, WXComponentConfig as the value storage configuration of each component.

@interface WXComponentConfig: WXInvocationConfig @property (nonatomic, strong) NSDictionary *properties; @end @interface WXInvocationConfig: NSObject @property (nonatomic, strong) NSString *name @property (nonatomic, strong); NSString *clazz; @property (nonatomic, strong) NSMutableDictionary *asyncMethods @property (nonatomic, strong); NSMutableDictionary *syncMethods; @end

WXComponentConfig inherited from the WXInvocationConfig, in the WXInvocationConfig store the component name name, class name clazz, the class inside the synchronization method dictionary syncMethods and asynchronous method dictionary asyncMethods.

Component registration is the key point here is the registration class method.

- registerMethods (void) {Class currentClass = NSClassFromString (_clazz); if (! CurrentClass) {WXLogWarning ("The module class [%@] @ doesn't exit! ", _clazz); return;} while (currentClass! = [NSObject class]) {unsigned int methodCount = 0; / / get the methods of a class list of Method = class_copyMethodList (object_getClass *methodList (currentClass, &); methodCount for (unsigned); int i = 0; I < methodCount; i++) {/ / get SEL NSString *selStr = [NSString stringWithCString:sel_getName string name (method_getName (methodList[i])) encoding:NSUTF8StringEncoding] BOOL; isSyncMethod = NO; / / if SEL name with sync, if synchronization method is ([selStr hasPrefix:@" wx_export_method_sync_ "]) {isSyncMethod = YES; / / SEL if the name is not sync, is different Step else method ([selStr hasPrefix:@} if "wx_export_method_"]) {isSyncMethod = NO;} else {/ / if the name without the wx_export_method_ prefix method, it is not exposed to the direct method, continue, for the next round of screening of continue; NSString} *name = nil, *method = nil; SEL selector = NSSelectorFromString (selStr); if ([currentClass respondsToSelector:selector]) {method = ((NSString* (*) (ID, SEL) [currentClass) methodForSelector:selector] (currentClass), selector if (method.length <);}; = 0) {WXLogWarning ("The module class [%@] @ doesn't has any met Hod! ", _clazz); continue;} / / remove method inside belt: No. NSRange range = [method rangeOfString:@": "]; if (range.location! = NSNotFound) {name} else {[method = substringToIndex:range.location]; name = method;} / / final dictionary which will in accordance with the asynchronous method and synchronization method to save the final methods the dictionary NSMutableDictionary *methods = isSyncMethod? _syncMethods: _asyncMethods; [methods setObject:method forKey:name];} free (methodList); currentClass = class_getSuperclass (currentClass);}}

Here the practice is also more conventional, find the corresponding class method, to determine whether the name with “sync” to determine whether the method is synchronous or asynchronous method. The emphasis here is on how the component method is converted into a class method.

Weex is done through the inside of the WX_EXPORT_METHOD macro exposure method.

#define WX_EXPORT_METHOD (method) WX_EXPORT_METHOD_INTERNAL (method, wx_export_method_) #define WX_EXPORT_METHOD_INTERNAL (method, token) / + (NSString *) WX_CONCAT_WRAPPER (token, __LINE__) {return / NSStringFromSelector (method / #define);} WX_CONCAT_WRAPPER (a, b) WX_CONCAT (a, b) #define WX_CONCAT (a, b) a ## B

WX_EXPORT_METHOD macros will fully expand into the following:

#define WX_EXPORT_METHOD (method) + (NSString *) wx_export_method_ __LINE__ {/ return NSStringFromSelector (/ method);}

For example, in the fifty-second line of WXWebComponent wrote the following lines of code:

WX_EXPORT_METHOD (@selector (goBack))

Then this macro will be expanded in the following manner:

(NSString *) wx_export_method_52 {return NSStringFromSelector (@selector (goBack));}

So in the WXWebComponent class method inside a more wx_export_method_52 method. Because in the same file, WX_EXPORT_METHOD macros are not allowed to write on the same line, so the name of the method will not change the same. However, the number of rows in different classes is not specified, the number of rows may be the same, so that different classes may have the same method name.

For example, in the WXScrollerComponent inside the fifty-eighth lines

WX_EXPORT_METHOD (@selector (resetLoadmore))

Fifty-eighth lines inside WXTextAreaComponent

WX_EXPORT_METHOD (@selector (focus))

This is the two different components, but the method of macro expansion after the name is the same, this kind of method two different classes, there is a name, but what will not, because it is through the class_copyMethodList access method, ensure that the list is the only name inside.

Another point to note is that although class_copyMethodList will get all of the methods (+ method), but some people may doubt that, through the + ordinary method of WX_EXPORT_METHOD macro exposed, will not be screened in?

Answer: Yes, will be obtained by class_copyMethodList, but there is a judge of the conditions, will avoid these not exposed to the external macro macro WX_EXPORT_METHOD + class method.

If that ordinary + method exposed through WX_EXPORT_METHOD macro, so there will not be prefix name with wx_export_method_, then the method is not exposed, the above code will directly continue screening, screening for the next round, so do not worry about those ordinary + No. class methods in interference.

Back to WXWebComponent registration, through the above method to get the end of the class method, the dictionary stored in the following information:

Methods = {goBack = goBack; goForward = goForward; reload = reload;}

This completes the first step of component registration, completing the registration configuration WXComponentConfig.

The second step of component registration, traversing all asynchronous methods.

- (NSMutableDictionary) _componentMethodMapsWithName: (NSString * name) {NSMutableDictionary *dict = [NSMutableDictionary dictionary]; NSMutableArray *methods = [NSMutableArray array]; [_configLock lock]; [dict setValue:methods forKey:@ "methods"]; WXComponentConfig *config = _componentConfigs[name]; void (^mBlock) (ID, ID, BOOL *) (id = mKey, ID ^ mObj, BOOL * mStop [methods {addObject:mKey]});; [config.asyncMethods enumerateKeysAndObjectsUsingBlock:mBlock]; [_configLock unlock]; return dict;}

Here is still called the WXComponentFactory method _componentMethodMapsWithName. This is the dictionary that traverses the asynchronous method and puts it back in the dictionary.

Or, in the case of the most complex WXWebComponent, here is the following asynchronous method dictionary:

{methods = (goForward, goBack, reload);}
Weex is how to run on the iOS client

The last step of the registration component is to register the component in JSFrame.

@interface WXSDKManager (*instanceDict) @property (nonatomic, strong) WXBridgeManager *bridgeMgr; @property (nonatomic, strong) WXThreadSafeMutableDictionary @end;

Inside the WXSDKManager will hold a strong WXBridgeManager. This WXBridgeManager is used to interact with the JS Bridge.

@interface WXBridgeManager: NSObject @property (nonatomic, weak, readonly) WXSDKInstance *topInstance @property (nonatomic, strong); WXBridgeContext *bridgeCtx; @property (nonatomic, assign) BOOL stopRunning @property (nonatomic, strong); NSMutableArray *instanceIdStack; @end

WXBridgeManager will be a weak reference to the WXSDKInstance instance, in order to be able to call some of the properties and methods of WXSDKInstance. WXBridgeManager one of the most important attribute is WXBridgeContext.

@interface (WXBridgeContext) @property (nonatomic, weak, readonly) WXSDKInstance *topInstance @property (nonatomic, strong); id< WXBridgeProtocol> jsBridge; @property (nonatomic, strong) WXDebugLoggerBridge * devToolSocketBridge (nonatomic, assign); @property BOOL debugJS; / / native to @property some methods to store called JS (nonatomic, strong) NSMutableDictionary *sendQueue some examples of @property; / / stack (nonatomic, strong) WXThreadSafeMutableArray *insStack; / / JSFramework logo is already loaded @property (nonatomic) BOOL frameworkLoadFinished; / / before JSFramework is loaded, the temporary storage method of @property (nonatomic, strong) NSMutableArray * methodQueue; / / service (@property storage JS template nonato Mic, strong) NSMutableArray *jsServiceQueue; @end

WXBridgeContext has a strong jsBridge. This is used to interact with the JS Bridge.

Weex is how to run on the iOS client

The relationship between the three is shown in fig.. Because it is a weak reference, it is represented by a virtual wireframe.

Back to the registration step, the following methods are invoked in WXSDKEngine:

[[WXSDKManager bridgeMgr] registerComponents:@[dict]];

WXBridgeManager call registerComponents method.

- (void) registerComponents: (NSArray * components) {if (components!) return __weak typeof (self); weakSelf = self; WXPerformBlockOnBridgeThread ((registerComponents:components]) ^ {[weakSelf.bridgeCtx};});

Finally, the WXBridgeManager inside the WXBridgeContext call registerComponents, the registration of components. However, this step of registering components is performed in a particular thread.

Void WXPerformBlockOnBridgeThread (void) (^block) ([WXBridgeManager _performBlockOnBridgeThread:block]) {}; + (void) _performBlockOnBridgeThread: (void) (^) (block ([NSThread) {if currentThread] = = [self jsThread]) {(block)} else {[self; performSelector:@selector (_performBlockOnBridgeThread:) onThread:[self jsThread] withObject:[block copy] waitUntilDone:NO];}}

As you can see, the block closure is performed in the jsThread thread, not the main thread. WXBridgeManager creates a new jsThread thread called @ com.taobao.weex.bridge @, all of which are registered in this thread. This jsThread is also a singleton, global uniqueness.

+ (NSThread * jsThread) {static dispatch_once_t onceToken; dispatch_once (& onceToken, WXBridgeThread = [[NSThread alloc] initWithTarget:[[self ^{class]sharedManager] selector:@selector (_runLoopThread) object:nil]; [WXBridgeThread setName:WX_BRIDGE_THREAD_NAME]; if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO (@ 8)) {[WXBridgeThread} else {setQualityOfService:[[NSThread mainThread] qualityOfService]]; [WXBridgeThread setThreadPriority:[[NSThread mainThread] threadPriority]] [WXBridgeThread start];};}); return WXBridgeThread;}

Here is the code to create jsThread, jsThread will @selector (_runLoopThread) as selector.

- (void) _runLoopThread currentRunLoop] addPort:[NSMachPort port] {[[NSRunLoop forMode:NSDefaultRunLoopMode]; while (_stopRunning!) {@autoreleasepool {[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];}}}

So here to open a jsThread runloop. Here is the open [NSMachPort port] mode of runloop, and then can not get to the port, but the runloop is not CFRunloop, so with the official documentation of the 3 methods have been unable to stop the runloop, only by way of while to stop. The above code is a way to write, of course, StackOverFlow recommended above is the following wording, the following is also used to write the way I write.

BOOL shouldKeepRunning = YES; *theRL = NSRunLoop / / global [NSRunLoop currentRunLoop] while (shouldKeepRunning; & & [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
- (void) registerComponents: (NSArray *) components {WXAssertBridgeThread (); if ((components) return); [self callJSMethod:@ "registerComponents";}

Registered in the WXBridgeContext component, in fact, is called the JS method “registerComponents”.

There is a need to pay attention to the point, because it is registered on the sub thread components, then if JSFramework is not loaded, native to call the JS method, the call must fail. So you need to be completed before the JSFramework load, the native call JS methods are cached, once the JSFramework load is completed, the cache method is lost to the JSFramework to load.

- (void) callJSMethod: (NSString * method) args: (NSArray * args) {if (self.frameworkLoadFinished) {[self.jsBridge} else {callJSMethod:method args:args]; [_methodQueue addObject:@{@ method: "method", "args": @ args}];}}

So in the WXBridgeContext need a NSMutableArray, used to cache the JSFramework load before the completion of the call to the JS method. This is in _methodQueue. If the JSFramework load is completed, the callJSMethod:args: method is called.

- (JSValue) callJSMethod: (NSString * method) args: (NSArray * args) {WXLogDebug ("Calling @ JS... Method:%@, args:%@", method, args); return [[_jsContext globalObject] invokeMethod:method withArguments:args];}

Since these registration methods are defined as global functions, then it is clear that the globalObject object should be raised on the JSContext method. The current process does not see the definition of the global function here, and you’ll see it later

Or to use WXWebComponent for example, then the registered component of the method is @ registerComponents, args parameters are as follows:

({append = tree; methods = (goForward, goBack, reload); type = web;})

In fact, the program runs here, and will not go to callJSMethod:args:, because JSFramework is not loaded yet.

The full flow of registered components is as follows:

Weex is how to run on the iOS client

Re registration Modules

Weex is how to run on the iOS client

The registration process of Modules is similar to that of Components.

(void + _registerDefaultModules) {[self registerModule:@ "DOM" withClass:NSClassFromString (@ WXDomModule)]; [self registerModule:@ "Navigator" withClass:NSClassFromString (@ WXNavigatorModule)]; [self registerModule:@ "stream" withClass:NSClassFromString (@ WXStreamModule)]; [self registerModule:@ "animation" withClass:NSClassFromString (@ WXAnimationModule)]; [self registerModule:@ "modal" withClass:NSClassFromString (@ WXModalUIModule)]; [self registerModule:@ "WebView" withClass:NSClassFromString (@ WXWebViewModule)]; [self registerModule:@ "instanceWrap" withClass:NSClassFromString (@ WXInstanceWrap)]; [self registerModule:@ "timer" withClass: NSClassFromString (@ WXTimerModule)]; [self registerModule:@ stor Age withClass:NSClassFromString (@ WXStorageModule)]; [self registerModule:@ "clipboard" withClass:NSClassFromString (@ WXClipboardModule)]; [self registerModule:@ "globalEvent" withClass:NSClassFromString (@ WXGlobalEventModule)]; [self registerModule:@ "canvas" withClass:NSClassFromString (@ WXCanvasModule)]; [self registerModule:@ "picker" withClass:NSClassFromString (@ "WXPickerModule" "[self registerModule:@"); meta withClass:NSClassFromString (@ WXMetaModule)]; [self registerModule:@ "webSocket" withClass:NSClassFromString (@ WXWebSocketModule)]};

WXSDKEngine will default to register these 15 basic modules. Here is a more complex module WXWebSocketModule, for example, to see how it is registered.

+ (void) registerModule: (NSString *) name clazz (Class) {WXAssert (name; & clazz, @ Fail register the module, please check if parameters the are correct to) &! "); / / 1 WXModuleFactory NSString *moduleName [WXModuleFactory registerModule:name registration module = withClass:clazz]; / / the 2 traversal of all synchronous and asynchronous method of NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName]; / / the 3 registration module to WXBridgeManager [[WXSDKManager bridgeMgr] registerModules:dict];}

Registration module is also divided into 3 steps, the first step is to register in the WXModuleFactory.

@interface WXModuleFactory (*moduleLock) @property (nonatomic, strong) NSMutableDictionary *moduleMap; @property (nonatomic, strong) NSLock @end;

In WXModuleFactory, moduleMap will store all the configuration information of the module, the registration process is also the process of generating moduleMap.

- (NSString *) _registerModule: (NSString *) name clazz (Class) {WXAssert (name & & clazz, @ Fail register the module, please check the parameters are if correct)! "); [_moduleLock lock]; / / note here is that the registration module is to allow the same module of the WXModuleConfig *config [[WXModuleConfig alloc] = init]; config.name = name; config.clazz = NSStringFromClass (clazz); [config registerMethods]; setValue:config forKey:name] [_moduleLock unlock] _moduleMap; return; name;}

The whole process is to register WXModuleConfig value, name key, deposited in the _moduleMap dictionary.

@interface WXModuleConfig: WXInvocationConfig @end

WXModuleConfig is only inherited from WXInvocationConfig, so it is exactly the same as WXInvocationConfig. The method of [config registerMethods] and the method of registering the component are the same method, and the specific registration process will not be repeated here.

The next WXModuleFactory will be recorded in WXModuleConfig:

_moduleMap = {animation = < WXModuleConfig: 0x60000024a230> canvas = "<"; WXModuleConfig:; 0x608000259ce0> clipboard; < WXModuleConfig: = 0x608000259b30> DOM = "<"; WXModuleConfig:; 0x608000259440> event; < WXModuleConfig: = 0x60800025a280> globalEvent = "<"; WXModuleConfig:; 0x60000024a560> instanceWrap; "< WXModuleConfig: = 0x608000259a70> meta =" < "; WXModuleConfig:; 0x60000024a7a0> modal; < WXModuleConfig: = 0x6080002597d0> navigator =" < "; WXModuleConfig:; 0x600000249fc0> picker; < WXModuleConfig: = 0x608000259e60> storage =" < "; WXModuleConfig:; 0x60000024a4a0> stream =" < ";; WXModuleConfig:; s 0x6080002596e0>" YncTest = < WXModuleConfig: 0x60800025a520> timer = "<"; WXModuleConfig:; 0x60000024a380> webSocket; < WXModuleConfig: = 0x608000259fb0> WebView = "<";; WXModuleConfig: 0x6080002598f0>} ";

All synchronous and asynchronous methods are recorded in each WXModuleConfig.

Config.name = DOM, config.clazz = WXDomModule, config.asyncMethods = {addElement = addElement:element:atIndex:; addEvent = addEvent:event:; addRule = addRule:rule:; createBody = createBody:; createFinish = createFinish; getComponentRect = getComponentRect:callback:; moveElement = moveElement:parentRef:index:; refreshFinish = refreshFinish; removeElement = removeElement:; removeEvent = "removeEvent:event:; scrollToElement; updateAttrs =" scrollToElement:options: "=" updateAttrs:attrs: "; updateFinish = updateFinish; updateStyle = updateStyle:styles:; config.syncMethods = {}},

The second step is to iterate through the list of methods.

- (NSMutableDictionary) _moduleMethodMapsWithName: (NSString * name) {NSMutableDictionary *dict = [NSMutableDictionary dictionary]; NSMutableArray *methods = [self _defaultModuleMethod]; [_moduleLock lock]; [dict setValue:methods forKey:name; WXModuleConfig *config = _moduleMap[name]; void (^mBlock) (ID, ID, BOOL ^ *) = (ID mKey, ID mObj, BOOL * mStop) {[methods addObject:mKey];}; [config.syncMethods enumerateKeysAndObjectsUsingBlock:mBlock]; [_moduleLock unlock]; enumerateKeysAndObjectsUsingBlock:mBlock] config.asyncMethods; return dict;}

The list of methods that traverse the module is different from the component. First, the module has a default method.

- (NSMutableArray*) _defaultModuleMethod {return arrayWithObjects:@ "addEventListener", @ removeAllEventListeners ", nil];}; [NSMutableArray

All modules have addEventListener and removeAllEventListeners methods. The second difference is that the module iterates through all the synchronous and asynchronous methods, which only traverse the asynchronous method. A dictionary of all methods that eventually returns the build module.

Take the DOM module as an example, it returns a dictionary as follows:

{DOM = (addEventListener, removeAllEventListeners, addEvent, removeElement, updateFinish, getComponentRect, scrollToElement, addRule, updateAttrs, addElement, createFinish, createBody, updateStyle, removeEvent, refreshFinish, moveElement);}

The final step is also in the WXBridgeManager registration module.

- (void) registerModules: (NSDictionary * modules) {if (modules!) return __weak typeof (self); weakSelf = self; WXPerformBlockOnBridgeThread ((registerModules:modules]) ^ {[weakSelf.bridgeCtx};});

The registration process is exactly the same as the component, and it is also operated in the jsThread “com.taobao.weex.bridge”.

- (void) registerModules: (NSDictionary *) modules {WXAssertBridgeThread (); if ((modules) return); [self callJSMethod:@ "registerModules";}

The name of the method that calls the JS here is changed to @ registerModules @, and the reference args is the second step method dictionary.

(args = {DOM = (addEventListener, removeAllEventListeners, addEvent, removeElement, updateFinish, getComponentRect, scrollToElement, addRule, updateAttrs, addElement, createFinish, createBody, updateStyle, removeEvent, refreshFinish, moveElement);})

Similarly, the module will not be registered at this time, because JSFramework is not loaded, it will be added to the methodQueue cache.

The full flow of the registration module is as follows:

Weex is how to run on the iOS client

The last is the registration of Handlers.

Weex is how to run on the iOS client
(void) + _registerDefaultHandlers registerHandler:[WXResourceRequestHandlerDefaultImpl new] {[self withProtocol:@protocol (WXResourceRequestHandler)]; [self registerHandler:[WXNavigationDefaultImpl new] withProtocol:@protocol (WXNavigationProtocol)]; [self registerHandler:[WXURLRewriteDefaultImpl new] withProtocol:@protocol (WXURLRewriteProtocol)]; [self registerHandler:[WXWebSocketDefaultImpl new] withProtocol:@protocol (WXWebSocketHandler)];}

Handler defaults to 4 WXSDKEngine.

+ (void) registerHandler: (ID) withProtocol: (Protocol *) protocol {WXAssert (handler & &, @ @ Fail register the handler, please check if parameters the are correct protocol) to! "); [WXHandlerFactory registerHandler:handler withProtocol:protocol];}

WXSDKEngine will continue to call the registerHandler:withProtocol: method of WXHandlerFactory.

@interface WXHandlerFactory: NSObject @property (nonatomic, strong) WXThreadSafeMutableDictionary *handlers; (void) + registerHandler: (ID) handler withProtocol: (Protocol * protocol); (ID) + handlerForProtocol: (Protocol *) protocol + (NSDictionary *); handlerConfigs; @end

WXHandlerFactory is also a single example, which has a thread safe dictionary handlers, to save the instance and Protocol name mapping table.

Weex is how to run on the iOS client

The final step in WXSDKEngine initialization is to perform JSFramework.

[[WXSDKManager bridgeMgr] executeJsFramework:script];

WXSDKManager will call WXBridgeManager to execute the SDK file inside the main.js.

- (void) executeJsFramework: (NSString * script) {if (script!) return __weak typeof (self); weakSelf = self; WXPerformBlockOnBridgeThread ((executeJsFramework:script]) ^ {[weakSelf.bridgeCtx};});

WXBridgeManager calls the executeJsFramework: method by WXBridgeContext. The method call is also in the child thread.

- (void) executeJsFramework: (NSString * script) {WXAssertBridgeThread (WXAssertParam); (script); WX_MONITOR_PERF_START (WXPTFrameworkExecute); [self.jsBridge executeJSFramework:script]; WX_MONITOR_PERF_END (WXPTFrameworkExecute); if ([self.jsBridge exception]) {NSString *message = [NSString stringWithFormat:@ JSFramework executes error:, [self.jsBridge exception]]% @ "; WX_MONITOR_FAIL (WXMTJSFramework, WX_ERR_JSFRAMEWORK_EXECUTE, message);} else {WX_MONITOR_SUCCESS (WXMTJSFramework); / / JSFramework is fully loaded so far completed self.frameworkLoadFinished = YES; / / [self implementation of all registered JsService executeAllJsService]; / / JSFramework version The JSValue *frameworkVersion [self.jsBridge callJSMethod:@ = "getJSFMVersion" args:nil]; if (frameworkVersion & & [frameworkVersion; isString]) {/ / the version number is stored in WXAppConfiguration [WXAppConfiguration setJSFrameworkVersion:[frameworkVersion toString]];} / / cache before the execution of all methods in the _methodQueue array for (NSDictionary *method in _methodQueue) {[self callJSMethod:method[@ "method" args:method[@ "args" [_methodQueue removeAllObjects] ";}; / / this initial work completed. WX_MONITOR_PERF_END (WXPTInitalize);};}

WX_MONITOR_PERF_START is marked before operation WXPTFrameworkExecute. After the completion of the JSFramework, use the WX_MONITOR_PERF_END tag to complete the execution.

- (void) executeJSFramework: (NSString * frameworkScript) {WXAssertParam (frameworkScript); if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO (@ 8)) {[_jsContext evaluateScript:frameworkScript withSourceURL:[NSURL URLWithString:@ "main.js"}else{"; [_jsContext evaluateScript:frameworkScript];}}

Loading JSFramework core code here, through the JSContext implementation of evaluateScript: to load JSFramework. Since there is no return value here, the purpose of the loaded JSFramework is to declare all the methods in it, and do not call the. This is also in line with the OC loading of other Framework process, the load is only loaded into memory, the Framework method can be called at any time, rather than a call on all its methods.

After loading the JSFramework, the JSService and JSMethod before the cache is loaded. JSService is cached in jsServiceQueue. JSMethod is cached in methodQueue.

- (void) executeAllJsService (NSDictionary *service in _jsServiceQueue {for *script = [service valueForKey:@) {NSString "script"]; NSString *name = [service valueForKey:@ "name"]; [self executeJsService:script withName:name];} [_jsServiceQueue removeAllObjects]};

JSService because it is a direct JS into NSString, so you can run directly executeJsService:withName.

For (NSDictionary *method in _methodQueue) {[self callJSMethod:method[@ "method" args:method[@ "args" [_methodQueue removeAllObjects] ";}; - (JSValue *) callJSMethod: (* NSString) method args: (NSArray * args) {WXLogDebug (" Calling @ JS... Method:%@, args:%@ ", method, args); NSLog (@ WXJSCoreBridge jsContext was about to call the method"); return [[_jsContext globalObject] invokeMethod:method withArguments:args];}

Since the _methodQueue is installed inside the global JS method, so the need to call invokeMethod: withArguments: to perform.

When all this load is completed, SDK initialization work is basically completed, it will mark the end of the WXPTInitalize.

It is also important to note how the jsBridge was loaded for the first time.

- (id< WXBridgeProtocol> jsBridge) {WXAssertBridgeThread ([WXDebugTool); _debugJS = isDevToolDebug]; Class = bridgeClass _debugJS? NSClassFromString (@ WXDebugger): [WXJSCoreBridge class]; if (_jsBridge & & [_jsBridge; isKindOfClass:bridgeClass]) {return _jsBridge;} if (_jsBridge) {[_methodQueue removeAllObjects] = NO; _frameworkLoadFinished = _jsBridge;} _debugJS? [NSClassFromString (@ WXDebugger) alloc]: [[WXJSCoreBridge alloc] init]; [self registerGlobalFunctions]; return _jsBridge;}

The first entry into this function does not have an instance of jsBridge, it will be an instance of the WXJSCoreBridge, followed by a global registration function. The two time when the function is called again, _jsBridge is WXJSCoreBridge type, it will be directly return, the following statement will not be repeated.

Typedef NSInteger (^WXJSCallNative) (NSString *instance, NSArray *tasks, NSString *callback); typedef NSInteger (^WXJSCallAddElement) (NSString *instanceId, NSString *parentRef, NSDictionary *elementData, NSInteger index); typedef NSInvocation (^WXJSCallNativeModule) * (NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *args, NSDictionary *options typedef (void); (^WXJSCallNativeComponent) NSString *instanceId, NSString * componentRef, NSString *methodName, NSArray *args, NSDictionary *options);

These 4 closures are the 4 global functions exposed to JS in the OC package.

- (void) registerCallNative: (WXJSCallNative) callNative (^callNativeBlock) {JSValue* (JSValue *, JSValue *, JSValue *) = ^JSValue* (JSValue *instance, JSValue *tasks, JSValue *callback) {NSString *instanceId = [instance toString]; NSArray *tasksArray = [tasks toArray]; NSString *callbackId = [callback toString]; WXLogDebug Calling native (@ instance:%@,... Tasks:%@, callback:%@, instanceId, tasksArray, callbackId); return [JSValue valueWithInt32: (int32_t) callNative (instanceId, tasksArray, callbackId) inContext:[JSContext currentContext]];}; _jsContext[@ "callNative"] = callNativeBlock;}
- (void) registerCallAddElement: (WXJSCallAddElement) callAddElement = callAddElementBlock ^ {ID (JSValue *instanceId, JSValue *ref, JSValue *element, JSValue *index, JSValue *ifCallback) {NSString *instanceIdString = [instanceId toString]; NSDictionary *componentData = [element toDictionary]; NSString *parentRef = [ref toString]; NSInteger insertIndex = [[index toNumber] integerValue]; WXLogDebug callAddElement ("% @, @...% @% @%ld, instanceIdString,,, parentRef, componentData, insertIndex) (long); return [JSValue valueWithInt32: (int32_t) callAddElement (instanceIdString, parentRef, componentData, insertIndex) inContext:[JSContext currentContext]] _jsContext[@ callAddElement";}; "= = callAddElementBlock;}
- (void) registerCallNativeModule: (WXJSCallNativeModule) callNativeModuleBlock {_jsContext[@ "callNativeModule"] = ^JSValue * (JSValue *instanceId, JSValue *moduleName, JSValue *methodName, JSValue *args, JSValue *options) {NSString *instanceIdString = [instanceId toString]; NSString *moduleNameString = [moduleName toString]; NSString *methodNameString = [methodName toString]; NSArray *argsArray = [args toArray]; NSDictionary *optionsDic = [options toDictionary]; WXLogDebug (@ callNativeModule...% @% @% @,,,, instanceIdString,% @ "moduleNameString, methodNameString, argsArray); NSInvocation *invocation = callNativeModuleBlock (instanceIdString, moduleNameString, methodNameString, Arg SArray, optionsDic); JSValue *returnValue = [JSValue inContext:[JSContext currentContext]]; return returnValue;};}
- (void) registerCallNativeComponent: (WXJSCallNativeComponent) callNativeComponentBlock {_jsContext[@ "callNativeComponent"] = ^void (JSValue *instanceId, JSValue *componentName, JSValue *methodName, JSValue *args, JSValue *options) {NSString *instanceIdString = [instanceId toString]; NSString *componentNameString = [componentName toString]; NSString *methodNameString = [methodName toString]; NSArray *argsArray = [args toArray]; NSDictionary *optionsDic = [options toDictionary]; WXLogDebug (@ callNativeComponent...% @% @% @,,,, instanceIdString,% @ "componentNameString, methodNameString, argsArray); callNativeComponentBlock (instanceIdString, componentNameString, methodNameString, args Array, optionsDic);};}

Because of the JS method of writing, a number of parameters are written in parentheses, and OC number of parameters in the middle: the number is not the same, all exposed to the JS, the need to re Block layer. The 4 methods of packing are as follows. Finally, the 4 methods are injected into the JSContext.

Weex is how to run on the iOS client

As shown above, the gray is the local OC incoming Block, the outer layer of the package, the method into JS, into the JSContext.

4 simulator WXSimulatorShortcutManager connect local debugging tools

#if TARGET_OS_SIMULATOR static dispatch_once_t onceToken; dispatch_once (& onceToken, ^{[WXSimulatorShortcutManager registerSimulatorShortcutWithKey:@ "I" modifierFlags:UIKeyModifierCommand UIKeyModifierAlternate action:^{NSURL *URL | = [NSURL URLWithString:@] "http://localhost:8687/launchDebugger" = [NSURLRequest NSURLRequest; *request requestWithURL:URL]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler: (NSData *data, NSURLResponse ^ *response, NSError *error) { }]; [task resume]; / /... (@ WXLogInfo "Launching browser..."); dispatch_after (dispatch_time (DISPATCH_TIME_NOW (int64_t) (1 * NSEC_PER_SEC)), dispatch_get_main_queue (websocket), ^{/ connection debugger [self connectDebugServer:@ "ws://localhost:8687/debugger/0/renderer"];})}); #endif;}];

Due to the normal development of the simulator may be used, then the debugging will be connected to the local browser (Chrome, Safari) debugging interface. This is the time to open the browser, and connect to the websocket debugger.

WXSDKEngine initialization of the entire process can be described as follows:

Weex is how to run on the iOS client

(two). How does Weex allow JS to tune the OC native UIView?

In the previous chapter we analyzed how WXSDKEngine was initialized, so how did the iOS Native client receive the JS page and call OC to generate UIView after the initialization was completed? In this chapter we analyze.

In the analysis of this problem, let’s take a look at the above Weex AppStore official to provide us with the example of the WeexPlayground scan code function is how to achieve two-dimensional code scanning can enter a page.

1 scan two-dimensional code principle

First look at some of the attributes of the scan code interface:

@interface WXScannerVC: UIViewController < AVCaptureMetadataOutputObjectsDelegate> @property (nonatomic, strong) AVCaptureSession * session (nonatomic, strong); @property AVCaptureVideoPreviewLayer *captureLayer; @end

This page does not have additional configuration, that is, some call the camera agent.

- (void) captureOutput: (AVCaptureOutput * captureOutput) didOutputMetadataObjects: (NSArray * metadataObjects) fromConnection: (AVCaptureConnection * connection) {[_captureLayer removeFromSuperlayer]; [_session stopRunning]; AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); if (metadataObjects.count > 0) {AVMetadataMachineReadableCodeObject = [metadataObjects * metadataObject objectAtIndex: 0]; [self openURL:metadataObject.stringValue];}}

When the scan to the two-dimensional code, the agent will call the above function, the scan out of the URL is metadataObject.stringValue.

- (void) openURL: (NSString* URL) {NSString *transformURL = URL; NSArray* = ELTs [URL componentsSeparatedByString:@ "?"]; if (elts.count > = 2; *urls = [elts.lastObject) {NSArray componentsSeparatedByString:@ "=" for "; (NSString *param in URLs if ([param) {isEqualToString:@" _wx_tpl "]) = {transformURL [[urls lastObject] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; break NSURL *url;}}} = [NSURL URLWithString:transformURL]; if ([self remoteDebug:url]) {return}; [self jsReplace:url]; WXDemoViewController * controller = [[WXDemoViewController alloc] init]; controller.url = URL; controll Er.source = @ "scan"; NSMutableDictionary *queryDict = [NSMutableDictionary new]; if (WX_SYS_VERSION_GREATER_THAN_OR_EQUAL_TO (@ 8)) {NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; NSArray *queryItems = [components queryItems]; for (NSURLQueryItem *item in queryItems) [queryDict setObject:item.value forKey:item.name];}else queryDict = {[self queryWithURL:url]}; NSString *wsport = queryDict[@ "wsport"]?: @ "8082"; NSURL *socketURL = [NSURL URLWithString:[NSString stringWithFormat:@ "ws://%@, url.host, wsport]]:% @"; controller.hotReloadSocket = [[SRWebSocket alloc] initWithURL:socketURL protocols:@[@ "echo-protocol"] Controller.hotReloadSocket.delegate = controller; [controller.hotReloadSocket open]; [[self pushViewController:controller animated:YES];}

Above this is the completion of the two-dimensional code page code, which contains some of the parameters to determine the URL query. Simplify it a little bit and simplify it to the following:

- (void) openURL: (NSString*) URL URL NSString *transformURL {/ / 1 for NSURL = URL; *url = [NSURL URLWithString:transformURL]; / / the 2 configuration of a new page of the URL WXDemoViewController * controller alloc] [[WXDemoViewController = init]; controller.url = URL; controller.source = @ "scan"; websocket *wsport = NSString / / 3 connection queryDict[@ "wsport"]? "8082": @ NSURL; *socketURL = [NSURL URLWithString:[NSString stringWithFormat:@ "ws://%@, url.host, wsport]]:% @"; controller.hotReloadSocket = [[SRWebSocket alloc] initWithURL:socketURL "echo-protocol" protocols:@[@ "; controller.hotReloadSocket.delegate = controller; [controller.hotReloadSocket open]; [[self navigationController] / / 4 page Jump PushViewController:controller animated:YES];}

OpenURL: in fact, do the 4 things mentioned above notes. The most important thing is to configure the new interface URL, as to connect websocket is to change the.We file or.Vue file can be seen on the phone in a timely manner changes. The last step is page jump. So scanning two-dimensional code can open a new page, the reason is only to the new page configuration of a URL, nothing more than that.

2.JS is how to tune the OC native View

Back to our topic again, JS exactly how to tune the OC native View?

All the secrets are in the WXSDKInstance class.

@interface WXSDKInstance: NSObject / viewController @property to render current (nonatomic, weak) UIViewController *viewController; / / Native root container View is completely controlled by the WXSDKInstance, developers can not change the @property (nonatomic, strong) UIView *rootView; / / if the component you want to fixed rootview frame, you can set this property to YES, when weex layout, rootview frame will not change. If set to NO @property (nonatomic, assign) BOOL isRootViewFrozen weex bundle scriptURL; / / @property (nonatomic, strong) NSURL *scriptURL Instance @property; / / father (nonatomic, weak) WXSDKInstance *parentInstance @property Instance; / / reference father node (nonatomic, weak) NSString *parentNodeRef; / / weex instance current is used to identify the one and only ID @property (nonatomic, strong) NSString *instanceId; / / the current state of the @property instance weex (nonatomic, assign) WXState state weex instance; / / when creating rootView callbacks when block (nonatomic, copy) @property (^onCreate) void (UIView *); / / frame change when the root container callback @property (nonatomic, copy void) (^onLayoutChange) (UIView *); / / when weex instance complete when rendering the callback bl Ock @property (nonatomic, copy) void (^renderFinish) (UIView *); / / weex instance when refresh is completed when the callback block @property (nonatomic, copy) void (^refreshFinish) (UIView *); / / when weex instance rendering failure callback block @ property (nonatomic, copy) void (^onFailed (NSError) *error); / / weex instance when the page scrolls callback block @property (nonatomic, copy) void (^onScroll) (CGPoint contentOffset); / / weex instance when rendering the callback block @property (nonatomic, copy) void (^onRenderProgress) (CGRect renderRect); / / weex instance frame @property (the nonatomic, assign CGRect frame / / Info); some information @property user storage (nonatomic, strong) NSMutableDictionary *userInfo; / / scaling factor @prope CSS pixel unit and equipment Rty (nonatomic, assign, readonly) CGFloat pixelScaleFactor; / / whether the monitoring component rendering @property (nonatomic, assign) BOOL trackComponent; (void) - renderWithURL: (NSURL * URL); - (void) renderWithURL: (NSURL * URL) options: (NSDictionary *) options data: (ID) data - (void) renderView:; (NSString * source) options: (NSDictionary *) options data: data (ID); / / forcedReload YES, each loaded will be re read, from URL to NO, read from the cache - (void) reload: (BOOL) forcedReload; (void) - refreshInstance: (ID) data - (void) destroyInstance; - (ID); moduleForClass: (moduleClass; Class) - (WXComponent *) componentForRef: (NSString * ref); - (NSUInteger) - (BOOL) numberOfComponents; checkModuleEventRegistered: (NSString*) event moduleClassName: (NSString*) moduleClassNa Me; - (void) fireModuleEvent: (Class) module eventName: (NSString *) eventName params: (NSDictionary*) params; (void) - fireGlobalEvent: (NSString * eventName) params: (NSDictionary * params); - (NSURL *) completeURL: (* NSString) URL; @end

A WXSDKInstance corresponds to a UIViewController, so each Weex page has a corresponding WXSDKInstance.

@property (nonatomic, strong) WXSDKInstance *instance;

WXSDKInstance is mainly used to render the page, usually by calling the renderWithURL method.

A Weex interface for active rendering process is as follows:

- render width (void) {CGFloat = self.view.frame.size.width; [_instance destroyInstance]; _instance [[WXSDKInstance alloc] = init]; _instance.viewController = self; _instance.frame = CGRectMake (self.view.frame.size.width-width, 0, width, _weexHeight); __weak typeof (self) weakSelf = self; _instance.onCreate = [weakSelf.weexView ^ (UIView *view) {removeFromSuperview]; weakSelf.weexView = view; [weakSelf.view addSubview:weakSelf.weexView]; UIAccessibilityPostNotification (UIAccessibilityScreenChangedNotification, weakSelf.weexView);}; _instance.onFailed = (NSError *error) {} ^ ^ (UIView; _instance.renderFinish = *view) {[weakSelf updateInstanceState:WeexIns TanceAppear];}; _instance.updateFinish = (UIView ^ *view) {}; if (! Self.url) {WXLogError ("error: render URL is @ nil"); return; NSURL *URL [self testURL: [self.url} = absoluteString]]; NSString = *randomURL [NSString stringWithFormat:@ "%@%@random=%d" URL.absoluteString, URL.query "&? @: @"; "?", arc4random ([_instance)]; renderWithURL:[NSURL URLWithString:randomURL] options:@{@ bundleUrl: URL.absoluteString} data:nil];}

Because WXSDKInstance is a support for real-time refresh, it is necessary to create the first to destroy the original, and then create a new.

WXSDKInstance support for the establishment of a variety of state callback callback function, the specific support for what state, you can see the definition of the above WXSDKInstance.

Weex supports loading from the local JS, also supports loading JS from the server. If you load from the local, you can use the following method to load a JSBundle from the local.

- (void) loadLocalBundle: (NSURL * URL * localPath) {NSURL = nil; pathComponents = NSMutableArray * nil; if (self.url) {pathComponents =[NSMutableArray arrayWithArray:[url.absoluteString pathComponents]]; [pathComponents removeObjectsInRange:NSRangeFromString (@ 03)]; [pathComponents replaceObjectAtIndex:0 withObject:@ "bundlejs"]; NSString *filePath = [NSString stringWithFormat:@, [NSBundle mainBundle].bundlePath, [pathComponents%@/%@ "componentsJoinedByString:@" / "[NSURL"; localPath = fileURLWithPath:filePath];}else = {NSString *filePath [NSString stringWithFormat:@ "%@/bundlejs/index.js" [NSBundle, mainBundle].bundlePath]; localPath = [NSURL fileURLWithPath:filePath *bundleUrl = NSString;} [NSURL fileURLWithPath:[NSString stringWithFormat:@ "%@/bundlejs/" [NSBundle mainBundle].bundlePath]].absoluteString renderWithURL:localPath "bundleUrl" [_instance; options:@{@ bundleUrl} data:nil];}

The final render page is done by calling renderWithURL:options:data.

- (void) renderWithURL: (NSURL * URL) options: (NSDictionary *) options data: (ID) {if (data! URL) {WXLogError ("Url must be passed @ if you use renderWithURL"); return; WXResourceRequest *request [WXResourceRequest requestWithURL:url resourceType:WXResourceTypeMainBundle} = "referrer:@" cachePolicy:NSURLRequestUseProtocolCachePolicy]; [self _renderWithRequest:request options:options data:data];}

When WXSDKInstance calls the renderWithURL:options:data: method, an WXResourceRequest is generated. NSMutableURLRequest is defined as follows:

@interface WXResourceRequest: NSMutableURLRequest @property (nonatomic, strong) ID taskIdentifier @property (nonatomic, assign); WXResourceType type; @property (nonatomic, strong) NSString *referrer @property (nonatomic, strong); NSString *userAgent; @end

WXResourceRequest is actually a layer of NSMutableURLRequest packaging.

Here to analyze the core function of the renderWithURL:options:data: (the following code in the source code based on a slight cut, the source is too long, after the deletion does not affect reading)

- (void) _renderWithRequest: (WXResourceRequest * request) options: (NSDictionary *) options data: (ID) data; *url {NSURL = request.URL; _scriptURL = URL; _options = options; _jsData = data; NSMutableDictionary = *newOptions [options mutableCopy]? [NSMutableDictionary: new]; WX_MONITOR_INSTANCE_PERF_START (WXPTJSDownload, self); __weak typeof (self) = weakSelf self; _mainBundleLoader = [[WXResourceLoader alloc] initWithRequest:request]; / / request callback _mainBundleLoader.onFinished = ^ (WXResourceResponse *response, NSData *data) {__strong typeof (weakSelf) strongSelf = weakSelf; if ([response isKindOfClass:[NSHTTPURLResponse class]] & & ((NSHTTPURLResponse) response).Sta TusCode! = 200) {NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code: ((NSHTTPURLResponse) response).StatusCode "message" userInfo:@{@ "status code error.": @}]; if (strongSelf.onFailed) strongSelf.onFailed (error) {return};}; if (! Data) {if (strongSelf.onFailed) {strongSelf.onFailed} (error); return; NSString *jsBundleString [[NSString alloc] initWithData:data} = encoding:NSUTF8StringEncoding]; if (! JsBundleString) {return}; [strongSelf _render WithMainBundleString:jsBundleString];}; / / request failed callback _mainBundleLoader.onFailed ^ (NSError = *loadError) {if (weakSelf.onFailed) {weakSelf.onFailed (loadError); [_mainBundleLoader start];}}};

As long as the above code is doing 2 things, the first step to generate the WXResourceLoader, and set its onFinished and onFailed callback. The second step calls the start method.

In WXSDKInstance strong hold of a WXResourceLoader, WXResourceLoader is defined as follows:

@interface WXResourceLoader: NSObject @property (nonatomic, strong) WXResourceRequest *request; @property (nonatomic, copy) void (^onDataSent) (unsigned long long bytesSent unsigned long long / * * /, * / / * totalBytesToBeSent); @ property (nonatomic, copy) void (^onResponseReceived) (const * WXResourceResponse); @property (nonatomic, copy (void) ^onDataReceived) (NSData *); @property (nonatomic, copy) void (^onFinished) (const, WXResourceResponse *, NSData *); @property (nonatomic, copy) void (^onFailed) (* NSError); - (instancetype) initWithRequest: (WXResourceRequest * request); - (void) start - (cancel: (void); NSError * * error @end);

WXResourceLoader contains a WXResourceRequest, so WXResourceRequest can also be seen on the network request package, and provides 5 different states of the callback callback function.

- (void) start if ([_request.URL isFileURL]) {[self} {_handleFileURL:_request.URL]; return; id< WXResourceRequestHandler> requestHandler = [WXHandlerFactory handlerForProtocol:@protocol (WXResourceRequestHandler)]; if (requestHandler) {[requestHandler sendRequest:_request withDelegate:self]}; else if ([WXHandlerFactory handlerForProtocol:NSProtocolFromString (@ WXNetworkProtocol)]) {/ / deprecated logic [self _handleDEPRECATEDNetworkHandler] else {}; WXLogError ("No resource request handler @ found!");}}

After calling the WXResourceLoader method of the start, it will first determine whether the local URL, if it is a local file, then start loading directly.

- (void) _handleFileURL: (NSURL * URL) {dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), NSData *fileData [[NSFileManager defaultManager] ^{= contentsAtPath:[url path]]; if (self.onFinished) {self.onFinished ([WXResourceResponse new], fileData);}}});

Local file directly callback onFinished function.

If it is not a local file, start the network request, request the server JS file.

- (void) sendRequest: (WXResourceRequest *) request withDelegate: (id< WXResourceRequestDelegate> delegate) {if (! _session) {NSURLSessionConfiguration *urlSessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; if ([WXAppConfiguration customizeProtocolClasses].count > 0) {NSArray *defaultProtocols = urlSessionConfig.protocolClasses; urlSessionConfig.protocolClasses = [[WXAppConfiguration customizeProtocolClasses] arrayByAddingObjectsFromArray: defaultProtocols] _session [NSURLSession sessionWithConfiguration:urlSessionConfig delegate:self;} = delegateQueue:[NSOperati OnQueue mainQueue]]; _delegates = [WXThreadSafeMutableDictionary new]; NSURLSessionDataTask *task dataTaskWithRequest:request]} = [_session; request.taskIdentifier = task; [_delegates setObject:delegate forKey:task]; [task resume];}

The network request here is the normal normal NSURLSession network request.

If successful, the final implementation of the callback function onFinished.

_mainBundleLoader.onFinished ^ (WXResourceResponse *response, NSData = *data) {__strong typeof (weakSelf) strongSelf = weakSelf; if ([response isKindOfClass:[NSHTTPURLResponse class]] & & ((NSHTTPURLResponse) response).StatusCode! = 200) {NSError *error = [NSError errorWithDomain:WX_ERROR_DOMAIN code: ((NSHTTPURLResponse) response).StatusCode userInfo:@{@ message: @ status code error.}]; if (strongSelf.onFailed) strongSelf.onFailed (error) {return};}; if (! Data) {NSString *errorMessage = [NSString stringWithFormat:@ Request to With no data R% @ Eturn, request.URL]; WX_MONITOR_FAIL_ON_PAGE (WXMTJSDownload, WX_ERR_JSBUNDLE_DOWNLOAD, errorMessage, strongSelf.pageName); if (strongSelf.onFailed) strongSelf.onFailed (error) {return};}; NSString *jsBundleString = "NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog (jsBundleString = @" download "% @ jsBundleString); if (! JsBundleString (WXMTJSDownload, WX_MONITOR_FAIL_ON_PAGE) {WX_ERR_JSBUNDLE_STRING_CONVERT, data converting to string failed. @" strongSelf.pageName ", return);} WX_MONITOR_SUCCESS_ON_PAGE (WXMTJSDownload, strongSelf.pageName); WX_MONITOR_INSTANCE_PE RF_END (WXPTJSDownload, strongSelf); [strongSelf _renderWithMainBundleString:jsBundleString];};

In the onFinished callback, there will be 3 kinds of error judgment, status code error, no data return, data converting to string.

NSString *jsBundleString = [[NSString initWithData:data encoding:NSUTF8StringEncoding]; [strongSelf _renderWithMainBundleString:jsBundleString]; alloc];

If everything is normal, then in the onFinished callback is actually get jsBundleString, and the implementation of the rendering operation.

- (void) _renderWithMainBundleString: (NSString * mainBundleString) {/ / the following code is deleted, remove some wrong judgment, but does not affect the reading of NSMutableDictionary *dictionary = [_options mutableCopy]; WXRootView WXPerformBlockOnMainThread (_rootView ^{generation / / [[WXRootView alloc] = initWithFrame:self.frame]; _rootView.instance = self; if (self.onCreate) {self.onCreate (_rootView);}}); / / module modules, components components, handlers default registration again, to ensure that in the creation of instance before they are registered with the [WXSDKEngine registerDefaults] createInstance [[WXSDKManager bridgeMgr] createInstance:self.instanceId; / / to template:mainBundleString options:dictiona Ry data:_jsData];}

WXSDKEngine will also re register the module modules, component components, handlers, to ensure that they are registered before the creation of instance.

- (void) createInstance: (NSString * instance) template: (NSString * temp) options: (NSDictionary *) options data: (ID data) {if (instance ||!! temp) return; if ([self.instanceIdStack! ContainsObject:instance]) {if ([options "RENDER_IN_ORDER" [@] boolValue]) {[self.instanceIdStack} else {[self.instanceIdStack insertObject:instance addObject:instance]; atIndex:0] __weak typeof;}} (self) = self (weakSelf; WXPerformBlockOnBridgeThread) {weakSelf.bridgeCtx [^ (createInstance:instance template:temp options:options data: Data];});}

WXSDKManager will call the createInstance:template:options:data: method, this method must also be implemented in JSThread.

- (void) createInstance: (NSString * instance) template: (NSString * temp) options: (NSDictionary *) options data: (ID) data ([self.insStack {if! ContainsObject:instance]) {if ([options[@ "RENDER_IN_ORDER" boolValue] [self.insStack]) {addObject:instance]} {[self.insStack else; insertObject:instance atIndex:0];}} //create a sendQueue bind to the current instance NSMutableArray *sendQueue [NSMutableArray [self.sendQueue setValue:sendQueue forKey:instance = array]; *args = NSArray; nil; if (data) {args = @[instance, temp, options? @{}: data]; else}, {args = @[instance, temp, options, @{}];}? [self callJSMethod:@ "createInstance" args:args];}

Eventually the JSContext call inside WXJSCoreBridge

[[_jsContext globalObject] invokeMethod:method withArguments:args];

Call JS “createInstance” method. Since then, began to call each other and JSFramework.

For example, let’s summarize the previous process:

Weex is how to run on the iOS client

Next, use an example to show how JS calls the OC native View.

First use JS to write a page:

< template> < div class= "container" > < image; src= "http://9.pic.paopaoche.net/up/2016-7/201671315341.png" class= "pic" onclick= "picClick" > < /image> < text class= "text" > {{title}}< /text> < /div> < /template> < style>.Container{align-items: center;}.Pic{width: 200px; height: 200px;}.Text{font-size: 40px; color: black; < /style>}; < script> module.exports = {data:{title:'Hello World', toggle:false, ready:function (console.log)}, {('this.title = ='+this.title) this.title ='Hello; Weex'; console.log ('This .title = ='+this.title);}, methods:{picClick: (function) {this.toggle = this.toggle; if! (this.toggle) {this.title = '}else{this.title' picture is clicked;'Hello = Weex';}}}} < /script>

This page runs up and looks like this:

Weex is how to run on the iOS client

The above is my.We source file, after the Weex compiler, it becomes a index.js, the code is as follows:

/ / {"framework": "Weex"} /******/ (function (modules) {/ / webpackBootstrap / / The module cache /******/ /******/ var installedModules = {}; / / The require function /******/ /******/ function __webpack_require__ (moduleId) {/ / Check if module is in /******/ /******/ cache if (installedModules[moduleId]) /******/ return installedModules[moduleId].exports; / / Create a new /******/ module (and put it into the cache) /******/ var module = installedModules[moduleId] = exports: {} {/******/ /******/, id: moduleId, loaded: false /******/ /******/ /******/}; / / Execute the module function modules[moduleId].call (MOD /******/ Ule.exports, module, module.exports, __webpack_require__); / / Flag the module as loaded /******/ /******/ module.loaded = true; / / Return the exports of the /******/ /******/ module return module.exports;} / / expose the /******/ /******/ modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; expose the module cache /******/ /******/ / / __webpack_require__.c / / __webpack_public_path__ = installedModules; /******/ /******/ __webpack_require__.p = ""; / / Load entry module / * *****/ and return exports return __webpack_require__ /******/ (0); /******/}) /************************************************************************/ /******/ ([0 / * * / /***/ function (module, exports, __webpack_require__) {var __weex_template__ = __webpack_require__ (1) var (2) __weex_style__ = __webpack_require__ var __weex_script__ = __webpack_require__ (3) __weex_define__ ('@weex-component/ 916f9ecb075bbff1f4ea98389a4bb514', [], function (__weex_require__, __weex_exports__, __weex_module__) {__weex_script__ (__weex_module__, __weex_exports__, __weex_require__) if (__weex_exports__.__esModule & & __weex_exports__.default) {__weex_module__.exports} = __weex_exports__.default __weex_module__.exports.template = __weex_template__ __weex_module__.exports.style = __weex_style__ __weex_bootstrap__ ('@weex-component/916f9ecb0)} 75bbff1f4ea98389a4bb514', undefined, undefined) /***/} / * * / 1, /***/ function (module, exports) {module.exports = {"type": "div", "classList", "container", "children": [{"type": "image", "attr": {"SRC": "http://9.pic.paopaoche.net/up/2016-7/201671315341.png"}, "classList": "pic", "events": {"click": "picClick"}}, {"type": "text", "classList", "text", "attr", "value": function {this.title}} ({return)}]}} / * * / 2 /***/, /***/ function (module, exports) { Module.exports = {{"container": "alignItems": "center"}, {"pic": "width": 200, "height", "text": 200}, {"fontSize": 40, "color": "#000000"}}} / * * / 3 /***/, /***/ (function module, exports) {module.exports = function (module, exports, __weex_require__) {'use strict'; module.exports data: function ({return) = {{title:'Hello World', toggle: false ready: function (}}, ready) {console.log ('this.title = = '+ this.title); this.title ='Hello Weex'; console.log ('this.title = = '+ this.title);}, {picClick: (picClick function methods: ) = {this.toggle! This.toggle; if (this.toggle) {this.title = 'click' picture is else ='Hello;} {this.title}}}}; Weex';} / * generated by weex-loader * /} /***/ /******/]);

Looks like a bunch of code, actually look carefully, you can see the doorway.

(function (modules) {/ / webpackBootstrap............ }

This code is automatically added for the time being. And then there are 4 sections of code, the beginning of the series were compiled, 0, 1, 3, 2. The 1, the 2 and the 3 segment code are corresponding to < template> < style> < script>. The above code is requested from the server down the code.

After the server gets JS, OC will call the JS method createInstance (ID, code, config, data) method.

Args: (0, "(here is the download of the JS on the network, too long, omitted), {bundleUrl =" http://192.168.31.117:8081/HelloWeex.js "; debug = 1;}

Then perform some conversion operations within the JSFramework:

[JS Framework] create an [email protected] instance from undefined [JS Framework] Intialize an instance; with: undefined [JS Framework] define a component; @weex-component/916f9ecb075bbff1f4ea98389a4bb514 [JS Framework] bootstrap for [; @ weex-component/916f9ecb075bbff1f4ea98389a4bb514 [[JS; Framework] "init" lifecycle in Vm (916f9ecb075bbff1f4ea98389a4bb514) [; [JS Framework] "created" lifecycle in Vm (916f9ecb075bbff1f4ea98389a4bb514) [[JS Framework] compile; native component by {"type": "div", "classList", "container", "children": [{"type": "image", "attr": {"SRC": "http://9.pic.paopaoche.net/up/2016-7/201671315341.png"}, "classList": "pic", "events": {"click": "picClick"}}, {"type": "text", "classList", "text", "attr": {}}]} [[JS; Framework] Compile to create body for div [JS Framework] compile to append; single node for {"ref": "_root", "type", "div", "attr", "style": {}, {"alignItems": "center"}}

Next JSFramework will call the callNative method of OC. Call the DOM module createBody method to create rootView. Parameters are as follows:

(args (attr = = {{{}; ref = "_root"; style = {alignItems = center; type = div;};}); method = createBody; module = DOM;})

After creating a good rootView, then continue to add View.

[JS Framework] compile native component by {"type": "image", "attr": {"SRC": "http://9.pic.paopaoche.net/up/2016-7/201671315341.png"}, "classList": "pic", "events": {"click": "picClick"}} [[JS Framework] compile to create; element for image; [JS Framework] compile to append single node for {"ref": "3", "type", "image", "attr": {"SRC": "http://9.pic.paopaoche.net/up/2016-7/201671315341.png"}, {"style": "width": 200, 200}: "height", "event", "click"]}

JSFramework continues to call OC’s callAddElement method to add View. Parameters are as follows:

{attr = {SRC = "http://9.pic.paopaoche.net/up/2016-7/201671315341.png";}; event = (click); ref = 3; style = {height = 200; width = 200; type = image;}};

UIImage after the completion of the add, then add UILabel.

[JS Framework] compile native component by {"type": "text", "classList", "text", "attr": [JS Framework] compile [{}}; to create element for text [JS Framework] compile to append; single node for {"ref": "4", "type": "text", "attr": {"value": "Hello World": "style"}, {"fontSize": 40, "color", "#000000"}}

JSFramework continues to call OC’s callAddElement method to add View. Parameters are as follows:

{attr = {value = "Hello World";}; ref = 4; style = {color = "#000000"; fontSize = 40;}; type =};}

When ready later:

[JS Framework] "ready" in Vm (916f9ecb075bbff1f4ea98389a4bb514) (lifecycle)

JSFramework continues to call the OC method of callNative, the parameters are as follows:

({args (= 4, {value = "Hello Weex";}); method = updateAttrs; module = DOM;})

So far, all the layouts have been completed. JSFramework will continue to call the callNative method of OC.

{args = (); method = createFinish; module = DOM;})

To this end, all View have been created. The final layout is as follows:

{layout: {width: 414, height: 672, top: 0, left: 0}, flexDirection:'column', alignItems:'stretch', flex: 0, width: 414, height: 672, left: 0, top: 0, children: [{_root:div layout: {width: 414, height: 672, top: 0, left: 0}, flexDirection:'column', alignItems:'center', flex: 0, width: 414, height: 672 children: [{3:image layout:, {width: 110.4, height: 110.4, top: 0, left: 151.8}, flexDirection:'column', alignItems:'stretch', flex: 0, width: 110.4, height: 110.4, {4:text layout: {width:}, 107.333, height: 26.6667, top: 110.4, left: 153.333}, flexDirection:'column', alignItems:'stretch', flex: 0,]},]}}.

From the final layout point of view, we can see that each module, component has its own unique ID.

The next step is to update the WXImageComponent image. After the end of the update, even if the entire Render complete.

JSFramework plays a role in the whole process is based on the input of JSBundle, the constant output Json format Virtual DOM, and then call the OC native method by JSCore, generate View.

In the example above, the working principle of the JSFramework is shown. The general process is as follows:

Weex is how to run on the iOS client

Then a detailed summary of the JSFramework in the whole Native is how to work.

  1. First of all, the initialization of the JSFramework will be initialized only once when the App is started, and multiple pages share this JSFramework. This design also improves the speed of opening all the pages of the Weex, JS Framework startup process of a few hundred milliseconds, equivalent to the opening of each page, these hundreds of milliseconds have been saved.
  2. Although there is only one global JSFramework, then Weex is how to avoid multiple Weex in the same JS Runtime do not affect each other? Weex has taken 2 measures, one is required for each Weex page must create a global unique instance ID, through this ID can directly correspond to a Weex page. Two is JS and Native call each other, each method requires the first parameter is ID. Such as createInstance (ID, code, config, data), sendTasks (ID, tasks), receiveTasks (ID, tasks). In this way, the state of the different pages is separated into different closures, so that they do not affect each other.
  3. When Native need to render the page, it will take the initiative to call createInstance (ID, code, config, data) method, where the code parameter is JS Bundle converted to String. JSFramework received this entry, it will begin to parse, and start sendTasks (ID, tasks).
  4. SendTasks (ID, tasks) will be called by the OC JSBridge Native method. Tasks will specify the module name, method name, and parameters of the function. For example: sendTasks (ID, module: [{‘dom’, method:’removeElement’, args: [elementRef]}]) before here will call to the OC method of the JSContext registration.
  5. The client will also call receiveTasks (ID, tasks) method, call the JS method. There are two types of receiveTasks, one is fireEvent, corresponding to the client to trigger a DOM element on the event, such as fireEvent (titleElementRef,’Click’, eventObject); the other is callback, which is the front after the function module calls the callback, for example we through the fetch interface to the Native to send a the HTTP request, and set up a callback function, this time, the first in the JS generates a callbackID for this callback function, such as string “123”, this is sent to the Native end of the callbackID, when the request after the end, native needs to request the results back to the JS Runtime, in order to be able to get around. This callback will eventually become similar to callback (callbackID, result) format.

Four. About Weex, ReactNative, JSPatch

Weex is how to run on the iOS client

This chapter is not in this article, but due to the recent Apple audit, brought some audit storm, so here to make a little.

When you read this article, pure ReactNative and pure Weex project has been able to perfect through the audit, JSPatch is still in the state of being blocked.

Since this article analyzes the working principle of Weex, then a little talk about the difference between RN, Weex and JSpatch.

First they three are based on the JS to repair the heat, but RN, Weex and JSPatch have a big difference is that if the Native does not provide the interface method can be used for JS calls it, then some methods in the RN and Weex interface could not achieve Native of the.

But JSPatch is different, although it is a set of JSCore based on bridge, but it is based on the Runtime, OC based Runtime, can achieve a variety of needs, even if the pre Native not exposed in the interface, you can add methods to realize the demand, can also change the already realization.

From the ability to update the hot point of view, the ability of RN and Weex is only moderate ability, and JSPatch is almost omnipotent, Runtime are implemented, it can be achieved.

So from the perspective of the ability to update the heat, RN and Weex can not change the Native native code, but also can not dynamically call the Native system private API. So Apple audits allow RN and Weex to pass.

Last

Weex is how to run on the iOS client

This article only describes the principle of how the Weex is run in iOS Native on the Weex side, but in fact there are many did not explain, for example to change a page element in the Vue.js page, Native page is how to make timely changes? Weex page is how to render the FlexBox algorithm? How is the front page packaged into JS bundle? How are.We and.Vue files translated by DSL? How to use JS Runtime to write some powerful JSService? WebpackBootstrap and weex-loader is how to generate the final JS code, in the middle of what optimization?……

The above issues will be in the next series of Weex articles which explain one after another, I hope everyone pointing!