Easy to understand JavaScriptCore

This article by our team tangled shoes.

Write in front

This article is for me a group share consolidation, most of the pictures are shot down directly from keynote, I have a lot of cool effect, see the blog of words depends on brain up, multi warning:)


Overview JavaScriptCore introduction Objective-C interacts with JavaScript JavaScript and Objective-C interactive memory management multithreading

I. JavaScriptCore profile

1.1 JavaScriptCore and JavaScriptCore framework

First of all, to distinguish between the JavaScriptCore and JavaScriptCore framework (with the text of the JSCore)

JavaScriptCore framework is an apple in the framework of the introduction of iOS7, the framework allows Objective-C and JavaScript code to interact directly become more simple and convenient.

JavaScriptCore is Apple’s Safari browser JavaScript engine, you may have heard of the Google V8 engine, WWDC Apple demonstrates the latest Safari, JavaScript is said to have already exceeded the processing speed of Google Chrome, which means that the performance of a JavaScriptCore is not lost V8.

JavaScriptCore framework is based on WebKit in C/C++ to achieve a JavaScriptCore packaging, in the old version of iOS development, many developers will also be the WebKit library into the project to compile the use of. Now iOS7 regards it as a standard library.

The JavaScriptCore framework is already existed in OS on the X platform, but the interface is pure C language, but before the iOS platform (before iOS7), apple did not open the framework, so many need to deal with JavaScript in iOS in app was compiled from JavaScriptCore.a open source WebKit, the interface is pure C language. Apple may be found more and more programs use self compiled JavaScriptCore, simply to oblige the JavaScriptCore framework open, while also providing a package of interface Objective-C.

This article will be discussed is based on the Objective-C package JavaScriptCore framework, that is, we use the JavaScriptCore framework to develop iOS app.

1.2 JavaScriptCore API Goals

Apple Objective-C package based on the JavaScriptCore interface has 3 objectives:

Easy to understand JavaScriptCore
JavaScriptCore API Goals
  • Automation: the use of these API, a lot of things are apple to help us do, such as the interaction between OC and JS, a lot of time will automatically help us to convert types.
  • Safety: we all know that JS is a dynamically typed language, that is to say that you transfer from JS to OC in value may be of any value, while OC is a statically typed language, it can dynamically receive various types of value, but you can not pass, does not collapse. Apple hopes these API is not easy to go wrong, even if wrong, but also will not lead to the collapse of the program, it is true. Another thing is that API itself is thread safe, and we’ll talk about it later.
  • High fidelity: the front two is better understood, but the high fidelity is how to explain it, very simple, is that Apple hopes that when we use these API to interact with JS, as in writing OC write OC, like writing JS write JS, do not need some strange words, this behind us with examples.

Two. Interaction between Objective-C and JavaScript

Easy to understand JavaScriptCore

Look at the small demo, a simple few lines of code, first of all, we introduced the JavaScriptCore framework, and then create the objects of a class called JSContext, and then use the JSContext implementation of a JS code of 2 + 2, where JS code is in the form of strings passed, after execution a value of type JSValue, finally, the values of type JSVlaue converted to integer and output.

The results are as follows, so we use OC to call a JS code, very simple:

Easy to understand JavaScriptCore

There are 2 demo before the class has not seen, one called JSContext, one called JSValue, the following we say a.

2.1 JSContext
  • JSContext is the execution environment of JS code
    JSContext provides a context for the execution of JS code, the JS code executed by jSCore must be implemented by JSContext.
  • JSContext
    JSContext corresponding to a global object in JS corresponds to a global object that corresponds to the browser window object has a GlobalObject attribute in JSContext, the JS code is executed in the GlobalObject, but in order to understand easily, JSContext can be equivalent to the global object.

You can think of him like that:

Easy to understand JavaScriptCore
JSContext
2.2 JSValue
  • JSValue is the name of the JS value of the
    JSValue package is the JS value. However, the value of the JS to OC is not directly with the needs of packaging, this is the JSValue value for JS packaging, a JSValue corresponds to a JS value, the JS value may be JS in number, the basic type Boolean, may also be the object, function, or even can be undefined or null. The following figure:
    Easy to understand JavaScriptCore OC-JS
    table
    in fact, equivalent to JS in the var.
  • JSValue
    exists in JSContext JSValue is not independent existence, it must exist in a certain JSContext, all elements like the browser are included in the Window object, a JSContext can contain multiple JSValue. Like this:
    Easy to understand JavaScriptCore
    JSValue

The OC (lambda) symbol in the Tips: diagram represents an anonymous function, the meaning of the closure, and its capital form is ^, which is why the Block definition in the.

  • is a strong reference to this point is critical, JSValue on its corresponding JS value and its subordinate JSContext objects are strongly cited relationship. Because jSValue needs these two things to execute the JS code, JSValue will hold them all the time.

The following figure can be more intuitive description of the relationship between them:

Easy to understand JavaScriptCore

Create a JSValue object using the following methods:

Easy to understand JavaScriptCore

you can convert the type in OC to the corresponding type in JS (see the previous type of comparison table) and wrap it in JSValue, including the basic types, Null and undfined.

Or you can create a new object, array, regular expression, error, these methods to achieve the effect is equivalent to writing in JS var = new Array ();

Can also be a OC object to the JS object, attribute and method but this conversion of the object, in the JS are not available, how to make JS properties and methods to obtain the OC object, behind us again.

2.3 actual use

Look at a Demo:
first is a section of the JS code, a simple recursive function, factorial calculation:

Easy to understand JavaScriptCore
Demo_JS

then what should we do if we want to call a function in the JS in OC? As follows:

Easy to understand JavaScriptCore
Demo_OC

first, load the JS code from bundle.

Then, create a JSContext and use it to execute the JS code, which is equivalent to the effect of a global object declared in a function called fatorial, but did not call it, just after the execution of this statement, so JS code does not return a value.

To get the function from the global object, JS function, here we use a dictionary like subscript notation to obtain the corresponding, as in a dictionary from the corresponding key value as simple, in fact, the JS object is to key storage properties of Value and JS in the form. The object object type, corresponding to the OC is a dictionary type, so this is natural and reasonable.

This dictionary like index can not only value, but also the value of. Not only can function in Context, also works with JSValue, he will fill in the brackets with key value to match the JSValue contains the values of JS have no corresponding attribute field, found on the back, did not find it returns undefined.

Then, we got the package the factorial function of the JSValue object, the callWithArguments method is called, you can call the function, the method of receiving an array as a parameter, this is because the parameters in the JS function is not fixed, we construct an array, and the NSNumber type 5 pass in the past, however, JS certainly did not know what NSNumber is, but don’t worry, JSCore will help us to automatically convert the corresponding type of JS, there will be NSNumber types 5 into number type JS in 5, and then to call this function (which is to say in front of the automation of the target in API).

Finally, if the function return value will function returns, if no return value is returned in the course undefined, after JSCore, these types of JS are packaged into JSValue, finally we get the JSValue object that is returned to the type and the corresponding output. The result here is 120, I’m not going to stick it out.

Three. Interaction between JavaScript and Objective-C

JavaScript interacts with Objective-C in 2 ways:

  • Block: the first way is to use block, block can also be called closures and anonymous functions, the use of block can be very convenient to OC single method exposed to the JS call, the specific implementation of our later.
  • JSExport protocol: the second way is to use the JSExport protocol, the OC can be directly exposed to the use of a JS object, but also in the use of JS as the object to call the same natural JS.

In short, Block is used to expose a single method, and the JSExport protocol can expose a OC object, we have to talk about these two ways.

3.1 Block

As mentioned above, the use of Block can be very convenient to the OC in a single method (ie Block) exposed to the JS call, JSCore will automatically wrap the Block into a JS method, specifically how to package it? Upper Demo:

Easy to understand JavaScriptCore

this is a OC Block exposed to JS code, is not very simple, like this, like this dictionary we use the wording of a OC Bock into the context, the block receives a parameter of type NSDictionary, and returns an object of type NSColor (NSColor is the class of APPkit, is used in the development of Mac, the equivalent of UIkit NSColor).

What will happen if you write like that? See figure

Easy to understand JavaScriptCore

We have a JSContext, then a OCBlock into account, JSCore will automatically in the global object (because it is assigned directly in Context, context corresponding to the global object) to create a function called makeNSColor, the Block package.

Then, in the JS, we call this exposed block, in fact, is a direct call to the packaging of the MakeNSColor Block method.

Easy to understand JavaScriptCore
Easy to understand JavaScriptCore

There is a
called colorForWord JS, it receives a word parameter, the colorMap is a JS object, which holds the name according to the color value color information, the color information is one of the JS object, the ColorForWord function is to obtain the corresponding color object through the color name. Then this function calls the MakeNSColor method, and introduced from colorMap according to the word field out of the color object, pay attention to the color of the object is a JS object, is a type of object, but we came in the Block receiver is a parameter of type NSDIctionary ah, don’t worry, then JSCore will automatically help us get the JS object type to NSDictionary type, and the like in front of the written, NSDictionary corresponds to JS in Object type.

Easy to understand JavaScriptCore

now, we have a wrapper Block JS function makeNSColor, and then there is a colorForWrod function to call it, the specific process is like this:

Easy to understand JavaScriptCore

Figure
looked from the left, colorForWrod called makeNSColor, passed parameter is JS Object type (taken from the colorMap color object), JSCore will pass the Object parameter into NSDictionary type, and makeNSColor used to call the internal packaging Block, Block returns a NSColor return value (NSObject) the type, JScore will be transformed into a wrapper Object (actually is JS Object type), returned to the colorForWrod.

What would it look like if we called the colorForWrod function in OC? The following figure:

Easy to understand JavaScriptCore

OC Caller to call the colorForWrod function, the function receives because colorForWrod is a String type of the parameter word, OC Caller passed is a parameter of type NSString, JSCore into the corresponding String type. Then the colorForWrod function to call down, as said above, know the return of wrapper Object to wrapper Object, it will return to call it OC Caller, JSCore and wrapper at this time to turn into a Object JSValue type conversion of the JSValue to call the corresponding method by then OC, you can get inside the packaging value here, we call the toObject method, the final will be a NSColor object, the object is from the beginning of return that exposure to JS in Block.

Through a step by step analysis, we found that JavaScriptCore will be at the junction of JS and OC transfer data to do the corresponding type conversion, conversion rules, such as the previous OC-JS type comparison table.

3.1.1 use Block pit

It is convenient to use the Block exposure method, but there are 2 pits need to pay attention to:

  • Do not use Block directly in JSValue
  • Do not use Block directly in JSContext

Because the Block strong reference external variables it is used inside, if you use the JSValue directly in the Block, then the JSvalue will be the Block strong references, and each JSValue is a strong reference that it belongs to JSContext, it is said, while the Block is injected into the Context so, the Block context will be a strong reference, this will result in a circular reference, causes a memory leak. Reasons for not using JSContext directly.

Easy to understand JavaScriptCore

So how to do it, for the first point, it is recommended to use JSValue as a parameter to the Block, rather than directly in the Block internal use, so that Block will not be a strong reference to the JSValue.

For second points, you can use the [JSContext currentContext] method to obtain the current Context.

3.2 JSExport protocol
3.2.1 introduction

Then JS and OC interaction in second ways: JSExport protocol, through the JSExport protocol can be very convenient to expose the OC object to the use of JS, and in the JS and use it as the JS object.

3.2.2 use

For a chestnut, we have a MyPoint class in Objective-C, which has two properties of the double type, x, y, an instance method description and a class method makePointWithX: Y:

Easy to understand JavaScriptCore

if we use the JSExport protocol to expose objects of this class to JS, how do we use the exposed JS object in JS? His property can be called directly, like the attribute JS object called the same, his example method can be called directly, like calling the JS object, and then his methods, can be directly used a global object directly call. Like ordinary JS, but the operation is a OC object.

Easy to understand JavaScriptCore

to achieve these only need to write such a sentence.

@protocol MyPointExports < JSExport>

Declare a custom protocol and inherit from the JSExport protocol. Then when you expose the object of this custom protocol to JS, the JS can use the OC object like the native object, which is the high fidelity of the API object.

Easy to understand JavaScriptCore

It should be noted that the OC function declaration format is not the same as in JS (it should be said that most of the language is not the same.) The OC function is a colon with a number of arguments: declared, which is obviously not directly exposed to the JS call, which is not high fidelity..

So we need to make some adjustments to the name of the method with parameters, when we expose a parameter with the OC method to JS, JSCore will use the following two rules to generate a corresponding JS function:

  • Remove all colons
  • The first lowercase letter followed by a colon

For example, the above class method, before the conversion method name should be makePointWithX:y:, JS generated in the corresponding method name will become makePointWithXY.

Apple is aware of this inconsistency may kill some obsessive-compulsive disorder.. So add a macro JSExportAs to deal with this situation, its role is: to JSCore in JS for the OC method generated by the corresponding method to specify the name.

For example, the above method or makePointWithX:y:, you can write:

Easy to understand JavaScriptCore

this makePoint is the name given to the JS method, so that the JS can be called directly in the makePoint to call the OC method makePointWithX:y.

Note: this macro is valid only for the OC method with parameters.

Then, there is a JSExport agreement to use the small Demo are interested can look at, in fact, very simple to use.

3.2.3 inquiry

However, the light will not be used, the JSExoprt agreement in the end what to do?

When you declare a custom protocol inherited from JSExport, you are telling JSCore that the attributes, instance methods, and class methods declared in this custom protocol need to be exposed to JS. The method that is not in this agreement will not be exposed

When you put the object that implements this protocol class of exposure to JS, JS will generate a corresponding JS object, then JSCore will be in accordance with the declaration of this agreement, to achieve this traversal category agreement, the agreement in the property declaration, into the properties of the JS object, is essentially convert getter and setter method, conversion method and said before block, create a JS package in the OC method, then the protocol statement instance methods into an instance method on the JS object, a global object on the JS conversion method.

Easy to understand JavaScriptCore

What exactly is a global object that is said here? This involves knowledge in JS:

  • Prototype & Constructor in the traditional Class based language such as Java, C++, the essence of inheritance is to extend an existing Class, and generate a new Subclass. However, there is no class type in JS, the JS how to achieve it, the answer is through the prototype object (Prototype). JavaScript sets up a prototype for each object that is created, pointing to its prototype object. Objects inherit properties and methods from their prototype objects. When we use the obj.xxx property to access an object when the JavaScript engine first in the current object to find the property, if not found, is to find the prototype object, if not found, has been traced back to the object.prototype object, finally, if it is not found, only to return undefined. A prototype object is an object that has a constructor Constructor that is used to create objects. If we have a Student constructor, and then use it to create a Xiaoming object, then the object of the prototype chain is this. Function Student (name) {this.name = name; this.hello = function (ALERT) {(‘Hello ‘+ this.name +’! ‘);}} var Xiaoming = new (Student’ Xiaoming —-&gt ‘Xiaoming); Student.prototype; —-> Object.prototype —-> null a little more detail below, Xiaohong is another the object of constructing Student function, the red arrow is the prototype chain. Note that the object of the Student.prototype is the prototype object of Xiaoming, Xiaohong, which also has a property constructor, which is directed to the Student function itself. In addition, Student function exactly the prototype object attribute prototype to Xiaoming, Xiaohong, Xiaoming, Xiaohong but these objects can not prototype this property, but you can use proto to view the non standard. So we think that Xiaoming, Xiaohong these objects inherited from Student. Xiaoming has a red arrow pointing to the Object.prototype on the prototype object, so we say that Student inherits from Object. The prototype object of Object.prototype points to null.
    Easy to understand JavaScriptCore JS
    prototype chain
    more about Prototype and Constructor knowledge can be seen here.

Don’t know that the prototype object here, a bit like OC in the class, and the constructor, is a bit like OC in the class, class OC is placed in the middle of a class, so that the global object is JS in the constructor.

Here I draw a diagram to describe the relationship between OC and JS when exposed to the JSExport protocol:

Easy to understand JavaScriptCore

We have a MyPoint class object point, when we use the JSExport protocol to the OC object exposed to JS, and JSCore first prototype object constructor generates a corresponding to the class in the context of JS, then JSCore will scan this class, the Declaration on the JSExport agreement exposed to JS property, (getter and setter) will be added to the prototype object, and the method will be added to this constructor, this place, it corresponds to the OC class and metaclass.

Then, as in the previous diagram, the prototype object has a constructor property that points to the constructor, which has a prototype attribute that points to the prototype object. We also know that the MyPoint class is inherited with the NSObject class, JSCore will also be exposed to the class of the parent class to create a prototype object and constructor, NSObject class prototype object is the Object JS class prototype object.

Each prototype object has an attribute called Prototype, capital of P, he is a pointer used to express inheritance in JS (a prototype chain), prototype prototype object MyPoint will point to the NSObject class. The prototype object of NSObject, and the prototype object of Object will point to null. Finally, the Mypoint class constructor and the prototype object are used to generate a JS object corresponding to the point object in OC in JS.

In this way, the JS is used to construct the class inheritance relationship with OC in JS.

This is the key to the use of the JSExport protocol exposed OC objects in JS can be like calling JS objects.

Four. Memory management

We all know that Objective-C is ARC (Automatic Reference Counting), can automatically resolve the circular reference issues (retain cycle), the programmer manual processing, and JavaScript is used in GC (Tracing Garbage Collection that is accurate), all cited are strong references, but the garbage collector will help me to solve circular reference issues, JavaScriptCore is the same, generally speaking, most of the time we don’t need to manually manage memory.

But the following 2 situations need to pay attention to:

  • Don’t give a OC object in the JS add member variables, the meaning of this sentence is to say, when we will be exposed to a OC object JS, as described above using the JSExport protocol, we can manipulate JS objects to manipulate OC objects, but this time, don’t let the OC in JS to add member variable, because the action of the consequences is that only in the JS to add an additional member variables for the OC object, but OC does not increase. So there is no point in doing so, there may be some strange memory management issues.
  • OC objects do not directly strong reference to the JSValue object, this words say frankly, is not directly to an object of type JSValue as attributes or member variables stored in a OC object, especially the OC object also exposed to JS time. This causes a circular reference. The following figure:
    Easy to understand JavaScriptCore

How to solve this problem? You might think that you can’t use a strong reference, and that’s a weak reference, like this, but it’s not enough, because JSValue doesn’t use an object to refer to him, and he will be released.

Easy to understand JavaScriptCore

What can we do? In this case, we need a weak reference relationship, because a strong reference causes a circular reference, but it is not allowed to be released without reference to the JSValue. In short, weak references can keep JSValue from being released.

So, Apple launched a new reference, called conditional retain, has a strong reference conditions, required to achieve our previous analysis can be referenced by this effect, and JSManagerValue is used to achieve the conditional Apple retain class.

Easy to understand JavaScriptCore

4.1 JSManagedValue

Easy to understand JavaScriptCore

this is the general use of JSManagerValue steps:

  • First, a JSManagerValue object is created by JSValue JSManagerValue, which is actually a JSValue object through the value property of a read-only access to it, this step is to add a JSValue to the weak reference.
  • If there is only the first step, the JSValue in the corresponding JS value after being released by the garbage collector, and this effect is like weak references, so also need to add a step in the virtual machine to add Owner to the JSManagerValue object (this virtual machine is to provide JS resources, will be more) and after doing so, to increase JSValue a strong relationship, as long as there is a little, the JSManagerValue which contains the JSValue will not be released: JSValue JS values are not garbage collector Owner object has not been released

To do so, that is, to avoid the cycle of reference, but also to ensure that the JSValue will not be released because of weak reference immediately.

Five. Multithreading

Said multi threading before you have to say another class JSVirtualMachine, which provides the underlying resources for the operation of JavaScript, has its own independent stack and garbage collection mechanism.

A container of JSVirtualMachine or JSContext, can contain a number of JSContext, in a process, you can have multiple JSVirtualMachine, which contains a number of JSContext, JSContext and several JSValue, including their relationship as below:

Easy to understand JavaScriptCore

It should be noted that, you can be in the same JSVirtualMachine different JSContext, mutual transmission of JSValue, but can no longer be different between the JSVirtualMachine JSContext JSValue.

Easy to understand JavaScriptCore

this is because each JSVirtualMachine has its own separate stack and garbage collector, a JSVirtualMachine garbage collector does not know how to deal with the value from another stack.

Said back to multi-threaded, JavaScriptCore provided by the API itself is thread safe.

You can create JSValue in different threads, and executing the JS statement with the JSContext, but when a thread is executing a JS statement, want to use the other thread is executing the JS statement JSContext the JSVirtualMachine will have to wait, wait before a thread is executed, in order to use this JSVirtualMachine.

Of course, this forced serial granularity is JSVirtualMachine, and if you want to execute JS code without thread, you can create different JSVirtualMachine for different threads.

Last but not least, it is about how to get the UIWebView in JSContext, due to the length of the problem here is not to repeat, recommend an article.

Easy to understand JavaScriptCore

Reference resources

Integrating JavaScript Native
Java Core Reference
Liao Xuefeng JavaScript tutorial in Apps API Script