原文:https://www.infinum.co/the-capsized-eight/articles/running-javascript-in-an-ios-application-with-javascriptcore

Although the JavaScriptCore framework has been officially available to iOS and Mac developers for quite some time now, its features are still documented rather poorly. Also, Apple provides no guidelines, save for a great WWDC session named "Integrating JavaScript into Native Apps".

However, while waiting for them to update the documentation, there's no reason not to take advantage of the framework's capabilities - it is, as will be demonstrated in this blog post, fairly easy to use.

Where to begin?

There are three object types we will use to provide a quick example of what you can do with JavaScriptCore. These are:

  • JSVirtualMachine
  • JSContext
  • JSValue


Keep in mind that the number of virtual machines is not limited by the number of ARM cores

JSVirtualMachine represents a JavaScript virtual machine, which manages memory and garbage collection. An arbitrary number of these objects can be instantiated in order to execute JavaScript code in parallel - each of them uses a separate thread.

JSContext sounds a bit trickier to decipher, but rest assured it makes sense. For JavaScript code to be executed, it needs to have a context, a space where it can create new variables and access existing ones. So, a JSContext is a code evaluation context, and you can have multiple contexts in a single virtual machine. You can also exchange information between contexts in the same virtual machine, but not between virtual machines.

JSValue is a class representing any type of data in a JavaScript context. You can have as manyJSValues as you want in a context and you can pass them between different contexts as long as those contexts belong to the same virtual machine.

How to use it?

To execute JavaScript code, we need to have a virtual machine and context. And here's how we can create a JavaScript context:

JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];

One thing worth mentioning is that the context is empty when we create it. It contains no variables or functions. There's more than one way to create variables in a context or assign values to them. Let's show the simplest one, literal assignment:

context[@"aNumber"] = @;
NSLog(@"%d", [context[@"aNumber"] toInt32]);

The console outputs 22.
Of course, we can also assign JSValues, NSNumbers and so on. But this example reveals an important detail: it's simple to access values within a context using the subscripting operator. We can also access values which were created or set by JavaScript code evaluated in the context. For example:

[context evaluateScript:@"var anotherNumber = aNumber + 20;"];
NSLog(@"%d", [context[@"anotherNumber"] toInt32]);

The console outputs 42.
Here we see how JavaScript code can be executed within the context. The aNumber variable which we created earlier is also available to the JavaScript code. However, if we were to store it in a JSValue, the JSValue would only contain its current value, and not a reference to the variable in the JavaScript context. In other words, it wouldn't change when the variable within the context changes, nor would the JavaScript variable change when the JSValue is changed.

Functions can also be JSValues. We can make use of that to expose an Objective-C block to a JavaScript context. For example, JavaScript's console.log() is not bridged automatically to the Xcode console. Here's how we can enable logging:

[context evaluateScript:@"var console = {};"]; // make console an empty object
context[@"console"][@"log"] = ^(NSString *message) {
NSLog(@"%@", message);
};

If we then write [context evaluateScript:@"console.log(anotherNumber)"];, the console will output42, as expected. This example shows how to use two-level subscripting to create properties that can be accessed from JavaScript via dot notation. It also shows how an Objective-C block can be assigned to a variable in a JavaScript context and how it can be called from within the context. The block can, of course, return a value and have an arbitrary number of arguments.

But it can go the other way around, too! It's possible to call a JavaScript function from Objective-C:

[context evaluateScript:@"var add = function(a, b) {return a + b;}"];
NSLog(@"%@", [context[@"add"] callWithArguments:@[@, @]]);
NSLog(@"%@", [context[@"add"] callWithArguments:@[context[@"aNumber"], context[@"anotherNumber"]]]);

The console will output 50 and 64, as expected. Now let's spice things up a bit with a more complicated function that still serves the same purpose:

[context evaluateScript:@"var addAndPrint = function(a, b) {console.log(add(a, b));}"];
[context[@"addAndPrint"] callWithArguments:@[@, @]];
[context[@"addAndPrint"] callWithArguments:@[context[@"aNumber"], context[@"anotherNumber"]]];

The console output is the same as it was in the previous example. The addAndPrint() function uses the previously declared add() function as well as console.log() which maps to an Objective-C block.

What next?

Thus far we have managed to create variables in the JavaScript context, assign values to them and call JavaScript functions and Objective-C blocks back and forth. While this is powerful by itself, there is more to JavaScriptCore.

A good example of what's possible is the JSExport protocol. If a class implements it, all of its properties and methods can be used in JavaScript contexts. Protocol inheritance makes it possible to exclude unwanted properties or methods from this behavior.

This feature is awesome because it allows you to assign an object to a JSValue and expose all or a part of its properties and methods to JavaScript. It also bypasses the aforementioned limitation of JSValue regarding changes to a JSValue on Objective-C side not propagating to the respective JavaScript variable and vice-versa.

Using the JSExport protocol, it's possible to have a reference to a mutable object both from Objective-C and JavaScript execution environments. The bridge between the two is implemented by JavaScript Core, so that each change to the object made in one execution environment gets copied immediately to the other one.

To explore this and other features and learn more about JavaScriptCore in general, refer to the WWDC session number 615, which targets this topic specifically.

If you decide to use the framework in a project, your JavaScript developers will probably want to know what they're working with. To avoid confusion, here's what JavaScriptCore's developers say about the project:

JavaScriptCore is the built-in JavaScript engine for WebKit. (...) JavaScriptCore is often referred with different names, such as SquirrelFish and SquirrelFish Extreme. Within the context of Safari, Nitro and Nitro Extreme (the marketing terms from Apple) are also commonly used. However, the name of the project and the library is always JavaScriptCore.

You should also note that the framework comes with a caveat: JIT compilation is not supported, although it isn't ruled out for future releases, either.

Why do it?

Apart from executing JavaScript code in native iOS applications being interesting and cool, I think it can be useful as well. Apple's example from the WWDC session suggests extending the application's functionality by using "modules" or "plug-ins" written in JavaScript.

To demonstrate the idea, they developed a simple syntax highlighting tool and a set of JS plug-ins that implemented the actual syntax highlighting logic for each programming language. Using this approach, they were able to support new languages just by loading additional plug-ins.

I believe a similar model could also be used to provide extensibility in applications that accomplish tasks using the same basic function set. Who knows, soon we may even see a simple JavaScript IDE as an iOS application.

[转]Running JavaScript in an iOS application with JavaScriptCore的更多相关文章

  1. iOS Application Security

    文章分A,B,C,D 4个部分. A) iOS Application Security 下面介绍iOS应用安全,如何分析和动态修改app. 1)iOS Application security Pa ...

  2. IOS Application Security Testing Cheat Sheet

    IOS Application Security Testing Cheat Sheet    [hide]  1 DRAFT CHEAT SHEET - WORK IN PROGRESS 2 Int ...

  3. JSPatch 可以让你用 JavaScript 书写原生 iOS APP

    简介   JSPatch 可以让你用 JavaScript 书写原生 iOS APP.只需在项目引入极小的引擎,就可以使用 JavaScript 调用任何 Objective-C 的原生接口,获得脚本 ...

  4. iOS Application Project与OS X Application Project对于plist使用的区别

    前几天因为在开源中国看到一个求源代码的问题: 模拟一个动物园系统MyZoo 1.动物园里面有三种动物:Panda,Elephant,Kangaroo 2.三种动物都有一定的数量(不止一只) 3.动物有 ...

  5. Persisting iOS Application Data in SQLite Database Using FMDB

    In previous articles we have utilized NSUserDefaults and .NET web services to persist iPhone data. N ...

  6. Attacking JavaScript Engines: A case study of JavaScriptCore and CVE-2016-4622(转)

    转:http://phrack.org/papers/attacking_javascript_engines.html Title : Attacking JavaScript Engines: A ...

  7. iOS Application Life Cycle 应用程序生命周期

    应用程序的状态 IOS的应用程序一共有5种状态. Not running(未运行):程序未启动 Inactive(未激活):其他两个状态切换时出现的短暂状态.唯一在此状态停留时间比较长的情况是:当用户 ...

  8. Demystifying iOS Application Crash Logs

    http://www.raywenderlich.com/23704/demystifying-ios-application-crash-logs This is a blog post by So ...

  9. iOS application/json上传文件等

    在和sever后台交互的过程中.有时候.他们需要我们iOS开发者以“application/json”形式上传. NSString *accessUrl = [NSString stringWithF ...

随机推荐

  1. 经Apache将tomcat转用80port这两个域名

    一般用tomcat通告Java web项目采用www.xxx.com:8080/appname/xxxservlet要访问一个简单的服务,这会'暴漏'应用程序名称(当然,你也可以摆脱),它看起来并不规 ...

  2. Linux下javaweb

    Linux下javaweb环境搭建 步骤: 1.使用远程工具连接上服务器,例如xsheel(ssh).filezilla(ftp) 2.JDK安装及相关配置 3.Mysql安装及相关配置 4.Tomc ...

  3. Java NIO的多路复用及reactor

    (from:http://developer.51cto.com/art/201112/306489.htm) 以下描述,为了说明问题,就提提历史(类似的东西,网上一搜一大把,但是希望你能在这里止步, ...

  4. 聊聊单元測试(一)——EasyMock

    一.单元測试是保证软件质量的重要方法. 单元測试是对系统中某个模块功能的验证,但我们总会遇到这样那样的问题,导致測试代码非常难编写.最直接的一个原因便是强耦合关系,被測试者依赖一些不easy构造,比較 ...

  5. ASP.NET学习笔记2--自己写代码绑定Gridview

    像以前一样,先写好自己的样式布局, 第二步,在数据库建立一个商品表 代码如下: CREATE TABLE [SHANGPING_INFO] ( [Shangping_Id] INT PRIMARY K ...

  6. [C#]设置或取消开机启动(注册表形式)

    原文:[C#]设置或取消开机启动(注册表形式) 使用代码: 代码效果:

  7. WCF消息交换模式之双工通讯(Duplex)

    WCF消息交换模式之双工通讯(Duplex) 双工通讯Duplex具有以下特点: 1它可以在处理完请求之后,通过请求客户端中的回调进行响应操作 2.消息交换过程中,服务端和客户端角色会发生调换 3.服 ...

  8. 微软不也是从Altair Basic这丑小鸭长成白天鹅吗?

    微软不也是从Altair Basic这丑小鸭长成白天鹅吗? February 2015 如果你想要弄清楚初创企业是怎么一回事的话,其中一个非常有价值的尝试是去研究下那些获得巨大成功的公司,去分析下为什 ...

  9. 查询DBlink创建

    DBlink创建 查询 博客分类: Oracle   当用户要跨本地数据库,访问另外一个数据库表中的数据时,本地数据库中必须创建了远程数据库的dblink,通过dblink本地数据库可以像访问本地数据 ...

  10. Android学习笔记:Home Screen Widgets(1):大约Widget

    什么是Home Screen Widgets Home screen Widget即称为小工具或者中文小工具,是显示在主页上的views,通过后台进程更新view的数据. Android由AppWid ...