搜尋
首頁web前端js教程在Swift中使用JavaScript的方法和技巧詳細介紹

本文作者Nate Cook是一位獨立的Web及行動應用開發者,是繼Mattt大神之後NSHipster的主要維護者,也是非常知名活躍的Swift博主,並且還是支持自動生成Swift在線文檔的SwiftDoc. org網站創造者。在本文中,他介紹了在Swift中使用JavaScript的方法和技巧,對於iOS和Web應用工程師有著非常實用的價值,以下為譯文:

在RedMonk發布的2015年1月在程式語言排行榜中,Swift採納率排名迅速飆升,從剛剛面世時的68位躍至22位,Objective-C仍然穩居TOP
10,而JavaScript則憑藉其在iOS平台上原生體驗優勢成為了年度最火熱的程式語言。

而早在2013年蘋果發布的OS X Mavericks和iOS 7兩大系統中便均已加入了JavaScriptCore框架,能夠讓開發者輕鬆、快捷、安全地使用JavaScript語言編寫應用程式。不論叫好叫罵,JavaScript霸主地位已成事實。開發者趨之若鶓,JS工具資源層出不窮,用於OS
X和iOS系統等高速虛擬機器也蓬勃發展。

JSContext/JSValue

JSContext即JavaScript程式碼的運作環境。一個Context就是一個JavaScript程式碼執行的環境,也叫作用域。當在瀏覽器中執行JavaScript程式碼時,JSContext就相當於一個窗口,可以輕鬆執行創建變數、運算甚至定義函數等的JavaScript程式碼:

//Objective-C
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"var num = 5 + 5"];
[context evaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"];
[context evaluateScript:@"var triple = function(value) { return value * 3 }"];
JSValue *tripleNum = [context evaluateScript:@"triple(num)"];
//Swift
let context = JSContext()
context.evaluateScript("var num = 5 + 5")
context.evaluateScript("var names = ['Grace', 'Ada', 'Margaret']")
context.evaluateScript("var triple = function(value) { return value * 3 }")
let tripleNum: JSValue = context.evaluateScript("triple(num)")

像JavaScript這類動態語言需要一個動態型別(Dynamic Type), 所以如程式碼最後一行所示,JSContext裡的不同值都封裝在JSValue物件中,包括字串、數值、陣列、函數等,甚至還有Error以及null和undefined。

JSValue包含了一系列用於取得Underlying Value的方法,如下表所示:

toStringNSStringString!booleantoBoolBOOLBoolnumbertoNumbertoDoubletoInt32NSNumberdoubleint32_t#NSNumber!DoubleInt32##Array##toArray[AnyObject]! toDictionaryNSDictionary[NSObject : AnyObject]!ObjecttoObjecttoObjectOfClass:自訂類型自訂類型#

想要检索上述示例中的tripleNum值,只需使用相应的方法即可:

//Objective-C
NSLog(@"Tripled: %d", [tripleNum toInt32]);
// Tripled: 30
//Swift
println("Tripled: \(tripleNum.toInt32())")
// Tripled: 30

下标值(Subscripting Values)

通过在JSContext和JSValue实例中使用下标符号可以轻松获取上下文环境中已存在的值。其中,JSContext放入对象和数组的只能是字符串下标,而JSValue则可以是字符串或整数下标。

//Objective-C
JSValue *names = context[@"names"];
JSValue *initialName = names[0];
NSLog(@"The first name: %@", [initialName toString]);
// The first name: Grace
//Swift
let names = context.objectForKeyedSubscript("names")
let initialName = names.objectAtIndexedSubscript(0)
println("The first name: \(initialName.toString())")
// The first name: Grace

而Swift语言毕竟才诞生不久,所以并不能像Objective-C那样自如地运用下标符号,目前,Swift的方法仅能实现objectAtKeyedSubscript()和objectAtIndexedSubscript()等下标。

函数调用(Calling Functions)

我们可以将Foundation类作为参数,从Objective-C/Swift代码上直接调用封装在JSValue的JavaScript函数。这里,JavaScriptCore再次发挥了衔接作用。

//Objective-C
JSValue *tripleFunction = context[@"triple"];
JSValue *result = [tripleFunction callWithArguments:@[@5] ];
NSLog(@"Five tripled: %d", [result toInt32]);
//Swift
let tripleFunction = context.objectForKeyedSubscript("triple")
let result = tripleFunction.callWithArguments([5])
println("Five tripled: \(result.toInt32())")

异常处理(Exception Handling)

JSContext还有一个独门绝技,就是通过设定上下文环境中exceptionHandler的属性,可以检查和记录语法、类型以及出现的运行时错误。exceptionHandler是一个回调处理程序,主要接收JSContext的reference,进行异常情况处理。

//Objective-C
context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
   NSLog(@"JS Error: %@", exception);
};
[context evaluateScript:@"function multiply(value1, value2) { return value1 * value2 "];
// JS Error: SyntaxError: Unexpected end of script
//Swift
context.exceptionHandler = { context, exception in
    println("JS Error: \(exception)")
}
context.evaluateScript("function multiply(value1, value2) { return value1 * value2 ")
// JS Error: SyntaxError: Unexpected end of script

JavaScript函数调用

了解了从JavaScript环境中获取不同值以及调用函数的方法,那么反过来,如何在JavaScript环境中获取Objective-C或者Swift定义的自定义对象和方法呢?要从JSContext中获取本地客户端代码,主要有两种途径,分别为Blocks和JSExport协议。

  • Blocks (块)

在JSContext中,如果Objective-C代码块赋值为一个标识符,JavaScriptCore就会自动将其封装在JavaScript函数中,因而在JavaScript上使用Foundation和Cocoa类就更方便些——这再次验证了JavaScriptCore强大的衔接作用。现在CFStringTransform也能在JavaScript上使用了,如下所示:

//Objective-C
context[@"simplifyString"] = ^(NSString *input) {
   NSMutableString *mutableString = [input mutableCopy];
   CFStringTransform((bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO);
   CFStringTransform((bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO);
   return mutableString;
};
NSLog(@"%@", [context evaluateScript:@"simplifyString('안녕하새요!')"]);
//Swift
let simplifyString: @objc_block String -> String = { input in
    var mutableString = NSMutableString(string: input) as CFMutableStringRef
    CFStringTransform(mutableString, nil, kCFStringTransformToLatin, Boolean(0))
    CFStringTransform(mutableString, nil, kCFStringTransformStripCombiningMarks, Boolean(0))
    return mutableString
}
context.setObject(unsafeBitCast(simplifyString, AnyObject.self), forKeyedSubscript: "simplifyString")

println(context.evaluateScript("simplifyString('안녕하새요!')"))
// annyeonghasaeyo!

需要注意的是,Swift的speedbump只适用于Objective-C block,对Swift闭包无用。要在一个JSContext里使用闭包,有两个步骤:一是用@objc_block来声明,二是将Swift的knuckle-whitening unsafeBitCast()函数转换为 AnyObject。

  • 内存管理(Memory Management)

代码块可以捕获变量引用,而JSContext所有变量的强引用都保留在JSContext中,所以要注意避免循环强引用问题。另外,也不要在代码块中捕获JSContext或任何JSValues,建议使用[JSContext currentContext]来获取当前的Context对象,根据具体需求将值当做参数传入block中。

  • JSExport协议

借助JSExport协议也可以在JavaScript上使用自定义对象。在JSExport协议中声明的实例方法、类方法,不论属性,都能自动与JavaScrip交互。文章稍后将介绍具体的实践过程。

JavaScriptCore实践

我们可以通过一些例子更好地了解上述技巧的使用方法。先定义一个遵循JSExport子协议PersonJSExport的Person model,再用JavaScript在JSON中创建和填入实例。有整个JVM,还要NSJSONSerialization干什么?

  • PersonJSExports和Person

Person类执行的PersonJSExports协议具体规定了可用的JavaScript属性。,在创建时,类方法必不可少,因为JavaScriptCore并不适用于初始化转换,我们不能像对待原生的JavaScript类型那样使用var person = new Person()。

//Objective-C
// in Person.h -----------------
@class Person;
@protocol PersonJSExports <JSExport>
    @property (nonatomic, copy) NSString *firstName;
    @property (nonatomic, copy) NSString *lastName;
    @property NSInteger ageToday;
    - (NSString *)getFullName;
    // create and return a new Person instance with `firstName` and `lastName`
    + (instancetype)createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName;
@end
@interface Person : NSObject <PersonJSExports>
    @property (nonatomic, copy) NSString *firstName;
    @property (nonatomic, copy) NSString *lastName;
    @property NSInteger ageToday;
@end
// in Person.m -----------------
@implementation Person
- (NSString *)getFullName {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}
+ (instancetype) createWithFirstName:(NSString *)firstName lastName:(NSString *)lastName {
    Person *person = [[Person alloc] init];
    person.firstName = firstName;
    person.lastName = lastName;
    return person;
}
@end
//Swift
// Custom protocol must be declared with `@objc`
@objc protocol PersonJSExports : JSExport {
    var firstName: String { get set }
    var lastName: String { get set }
    var birthYear: NSNumber? { get set }
    func getFullName() -> String
    /// create and return a new Person instance with `firstName` and `lastName`
    class func createWithFirstName(firstName: String, lastName: String) -> Person
}
// Custom class must inherit from `NSObject`
@objc class Person : NSObject, PersonJSExports {
    // properties must be declared as `dynamic`
    dynamic var firstName: String
    dynamic var lastName: String
    dynamic var birthYear: NSNumber?
    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
    class func createWithFirstName(firstName: String, lastName: String) -> Person {
        return Person(firstName: firstName, lastName: lastName)
    }
    func getFullName() -> String {
        return "\(firstName) \(lastName)"
    }
}
  • 配置JSContext

创建Person类之后,需要先将其导出到JavaScript环境中去,同时还需导入Mustache JS库,以便对Person对象应用模板。

//Objective-C
// export Person class
context[@"Person"] = [Person class];
// load Mustache.js
NSString *mustacheJSString = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil];
[context evaluateScript:mustacheJSString];
//Swift
// export Person class
context.setObject(Person.self, forKeyedSubscript: "Person")
// load Mustache.js
if let mustacheJSString = String(contentsOfFile:..., encoding:NSUTF8StringEncoding, error:nil) {
    context.evaluateScript(mustacheJSString)
}
  • JavaScript数据&处理

以下简单列出一个JSON范例,以及用JSON来创建新Person实例。

注意:JavaScriptCore实现了Objective-C/Swift的方法名和JavaScript代码交互。因为JavaScript没有命名好的参数,任何额外的参数名称都采取驼峰命名法(Camel-Case),并附加到函数名称上。在此示例中,Objective-C的方法createWithFirstName:lastName:在JavaScript中则变成了createWithFirstNameLastName()。

//JSON
[
    { "first": "Grace",     "last": "Hopper",   "year": 1906 },
    { "first": "Ada",       "last": "Lovelace", "year": 1815 },
    { "first": "Margaret",  "last": "Hamilton", "year": 1936 }
]
//JavaScript
var loadPeopleFromJSON = function(jsonString) {
    var data = JSON.parse(jsonString);
    var people = [];
    for (i = 0; i < data.length; i++) {
        var person = Person.createWithFirstNameLastName(data[i].first, data[i].last);
        person.birthYear = data[i].year;
        people.push(person);
    }
    return people;
}
  • 动手一试

现在你只需加载JSON数据,并在JSContext中调用,将其解析到Person对象数组中,再用Mustache模板渲染即可:

//Objective-C
// get JSON string
NSString *peopleJSON = [NSString stringWithContentsOfFile:... encoding:NSUTF8StringEncoding error:nil];
// get load function
JSValue *load = context[@"loadPeopleFromJSON"];
// call with JSON and convert to an NSArray
JSValue *loadResult = [load callWithArguments:@[peopleJSON]];
NSArray *people = [loadResult toArray];
// get rendering function and create template
JSValue *mustacheRender = context[@"Mustache"][@"render"];
NSString *template = @"{{getFullName}}, born {{birthYear}}";
// loop through people and render Person object as string
for (Person *person in people) {
   NSLog(@"%@", [mustacheRender callWithArguments:@[template, person]]);
}
// Output:
// Grace Hopper, born 1906
// Ada Lovelace, born 1815
// Margaret Hamilton, born 1936
//Swift
// get JSON string
if let peopleJSON = NSString(contentsOfFile:..., encoding: NSUTF8StringEncoding, error: nil) {
    // get load function
    let load = context.objectForKeyedSubscript("loadPeopleFromJSON")
    // call with JSON and convert to an array of `Person`
    if let people = load.callWithArguments([peopleJSON]).toArray() as? [Person] {

        // get rendering function and create template
        let mustacheRender = context.objectForKeyedSubscript("Mustache").objectForKeyedSubscript("render")
        let template = "{{getFullName}}, born {{birthYear}}"

        // loop through people and render Person object as string
        for person in people {
            println(mustacheRender.callWithArguments([template, person]))
        }
    }
}
// Output:
// Grace Hopper, born 1906
// Ada Lovelace, born 1815
// Margaret Hamilton, born 1936


JavaScript Type
##JSValue method
Objective-C Type
Swift Type
string
toUInt32

uint32_t

UInt32

#至今 #NSDate NSDate!
NSArray

以上是在Swift中使用JavaScript的方法和技巧詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
苹果发布用于同态加密的开源 Swift 软件包,已部署在 iOS 18 中苹果发布用于同态加密的开源 Swift 软件包,已部署在 iOS 18 中Jul 31, 2024 pm 01:10 PM

7月31日消息,苹果公司昨日(7月30日)发布新闻稿,宣布推出新的开源Swift包(swift-homomorphic-encryption),用于在Swift编程语言中启用同态加密。注:同态加密(HomomorphicEncryption,HE)是指满足密文同态运算性质的加密算法,即数据经过同态加密之后,对密文进行特定的计算,得到的密文计算结果在进行对应的同态解密后的明文等同于对明文数据直接进行相同的计算,实现数据的“可算不可见”。同态加密技术可以计算加密数据,而且不会向操作过程泄露底层的未加

es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

Vue.js与Swift语言的集成,实现高级iOS应用的开发和测试的建议Vue.js与Swift语言的集成,实现高级iOS应用的开发和测试的建议Aug 01, 2023 am 09:53 AM

Vue.js是一种流行的JavaScript框架,用于构建用户界面。而Swift语言是一种用于iOS和macOS应用程序开发的编程语言。在本文中,我将探讨如何将Vue.js与Swift语言集成,以实现高级iOS应用程序的开发和测试。在开始之前,我们需要确保你已经安装了以下软件和工具:Xcode:用于开发和编译iOS应用程序的集成开发环境。Node.js:用于

如何使用MySQL在Swift中实现数据导入和导出功能如何使用MySQL在Swift中实现数据导入和导出功能Aug 01, 2023 pm 11:57 PM

如何使用MySQL在Swift中实现数据导入和导出功能导入和导出数据是许多应用程序中常见的功能之一。本文将展示在Swift语言中使用MySQL数据库实现数据导入和导出的方法,并提供代码示例。要使用MySQL数据库,首先需要在Swift项目中引入相应的库文件。你可以通过在Package.swift文件中添加以下依赖来实现:dependencies:[

如何使用Redis和Swift开发实时聊天功能如何使用Redis和Swift开发实时聊天功能Sep 20, 2023 pm 12:31 PM

如何使用Redis和Swift开发实时聊天功能引言:实时聊天功能已经成为现代社交应用中不可或缺的一部分。在开发社交应用时,我们经常需要使用实时聊天来提供用户之间的互动和信息交流。为了达到实时性和高可用性的要求,我们可以使用Redis和Swift来开发这样一个功能。Redis简介:Redis是一个开源的内存数据结构存储系统,也被称为数据结构服务器。它通过提供多

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

与Go接近的编程语言有哪些?与Go接近的编程语言有哪些?Mar 23, 2024 pm 02:03 PM

与Go接近的编程语言有哪些?近年来,Go语言在软件开发领域逐渐崭露头角,受到越来越多开发者的青睐。虽然Go语言本身拥有简洁、高效和并发性强的特点,但有时候也会遇到一些限制和不足。因此,寻找与Go语言接近的编程语言成为了一种需求。下面将介绍一些与Go语言接近的编程语言,并通过具体代码示例展示它们的相似之处。RustRust是一种系统编程语言,注重安全性和并发性

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具