【翻譯】Swift 與 JavaScript 的交互

本篇文章由我們團(tuán)隊(duì)的小馬童鞋翻譯完成,原文地址:From Swift to Javascript and Back

我承認(rèn),我喜歡用 JavaScript 來開發(fā) web 程序。有很多庫是用 JavaScript 來寫的,坦白的說,我認(rèn)為,在其他語言上在實(shí)現(xiàn)一遍的話然爆,非常的2∈蛲迹可能你要猜到了曾雕,我要說的就是,把 JavaScript 庫整合到你的 Swift 代碼中助被,并在 Swift 和 JavaScript 之間實(shí)現(xiàn)數(shù)據(jù)的共享和方法的相互調(diào)用剖张。

為了保持文章不會太長,我將簡明的介紹幾個(gè)基礎(chǔ)元素然后把主要把精力放在示例上揩环。

JSVirtualMachine

JSVirtualMachine 是一個(gè)執(zhí)行 JavaScript 代碼的封閉環(huán)境搔弄。它主要有兩個(gè)目的

  1. 允許你在獨(dú)立的線程中并行的執(zhí)行代碼
  2. 允許你清理所分配的 JavaScript 和 OC/Swift 橋接的內(nèi)存

在 JavaScript 中做的任何事情,最終都會在一個(gè) JSVirtualMachine 中執(zhí)行丰滑。當(dāng)你需要創(chuàng)建一個(gè)獨(dú)立的線程時(shí)顾犹,你需要為每一個(gè) 線程單獨(dú)創(chuàng)建一個(gè)JSVirtualMachine,因?yàn)? JavaScriptCore Api 是線程安全的褒墨。直到初始化線程結(jié)束炫刷,你才可以在單獨(dú)的線程中訪問在 JSVirtualMachine 中的代碼,如果你在后臺線程中這么做的話郁妈,那么浑玛,你將無法得到預(yù)期的結(jié)果。

對于內(nèi)存問題還有一個(gè)問題噩咪,當(dāng)你使用完一個(gè)對象時(shí)锄奢,JSVirtualMachine ?提供了釋放這個(gè)對象的機(jī)制。有一個(gè)壞消息就是剧腻,任何時(shí)候,你將一個(gè) OC/Swift 對象?暴露給 JavaScript 并且存儲的時(shí)候涂屁,將會產(chǎn)生一個(gè)循環(huán)引用书在。同樣,你將一個(gè) JavaScript 值存儲為 OC/Swift 對象的時(shí)候拆又,也將產(chǎn)生循環(huán)引用儒旬。循環(huán)引用的產(chǎn)生栏账,是因?yàn)?JSValue 為了在 native 中訪問底層的 JavaScript 值所持有了一個(gè)強(qiáng)引用,同樣的栈源, 一個(gè) context 持有你所傳到 JavaScript 中的 任何 OC/Swift 對象的強(qiáng)引用挡爵。當(dāng)你用你的對象做完某些事情之后,你應(yīng)當(dāng)用 JSVirtualMachine 的 removeManagedReference:withOwner 方法去清理內(nèi)存甚垦。特別的茶鹃,你應(yīng)當(dāng)用 JSManagedValue 在 native 中有條件的存儲 JSValue 。JavaScript 的垃圾回收器將回收那些你移除強(qiáng)引用的對象艰亮。

JSContext

一個(gè) JSContext 對象就是 JavaScript 代碼的運(yùn)行環(huán)境闭翩,這和瀏覽器的 window 對象很相似。在同一個(gè) context 中迄埃,被加到這個(gè) context 中的任何對象疗韵,都會可以被其他的對象無障礙的訪問。實(shí)際上侄非,是沙盒控制范圍內(nèi)的 JavaScript 的變量蕉汪,方法,對象逞怨。你可以執(zhí)行 JavaScript 和 OC/Swift 代碼者疤,訪問 JavaScript 中的值,可以通過 JSContext 把一些值和對象骇钦,從 OC/Swift 傳遞到 JavaScript宛渐。

JSValue

JSValue 是 JavaScript 值的一個(gè)封裝。它像一個(gè)橋梁一樣可以使你在 JavaScript 和 OC/Swift 之間傳遞和共享數(shù)據(jù)眯搭。一個(gè) JSValue 持有一個(gè)在他所屬的 JSContext 的一個(gè)強(qiáng)引用窥翩。作為警告,你應(yīng)該記住鳞仙,如果你想在 OC/Swift 中存儲 JSValue 的時(shí)候寇蚊,將會產(chǎn)生循環(huán)引用。另外棍好,為了訪問底層的 JavaScript 對象仗岸,你可以使用 JSValue 創(chuàng)建一個(gè)被封裝為 OC/Swift 對象的JavaScript 對象。同樣借笙,你也可以創(chuàng)建用 OC/Swift 寫的 JavaScript 方法扒怖。

JSManagedValue

JSManagedValue 是一個(gè) JSValue 附加了一些額外的邏輯,使你在本地存儲一些底層的 JavaScript 對象的時(shí)候不會引起循環(huán)引用业稼。它和 ARC 有點(diǎn)類似盗痒,它的內(nèi)存管理可以在離開作用域的時(shí)候自動釋放對象。JSManagedValue 存在于以下兩種情況下低散。

  1. 這個(gè)值仍然是底層的 JavaScript 對象俯邓。
  2. 被用 addManagedReference:withOwner 添加到 JSManagedValue 并且沒有被 removeManagedReference:withOwner 方法移除骡楼。

否則的話 JSManagedValue 會被置為 nil ,釋放 JSValue, 在 JavaScript 那邊可以被垃圾回收稽鞭。

JSExport

JSValue 可以把 JavaScript 內(nèi)置的類型轉(zhuǎn)為 OC/Swift 對象鸟整,同樣也可以吧 OC/Swift 對象轉(zhuǎn)為 JavaScript 類型。但是朦蕴,在沒有其他幫助下篮条,JSValue 不能把 OC/Swift 的類轉(zhuǎn)為 JavaScript 對象。JSExport 這個(gè) protocol 提供了一種方式來把 OC/Swift 的實(shí)例方法梦重,類方法兑燥,屬性,轉(zhuǎn)為 JavaScript 對象琴拧。

默認(rèn)使用 JSValue 的時(shí)候降瞳,JavascriptCore 將把 OC/Swift 類轉(zhuǎn)為 JavaScript 對象,但不會暴露其是實(shí)例方法蚓胸,類方法挣饥,屬性。你可以在 JSExport protocol 中定義那些方法想要暴露給 JavaScript

JavaScriptCore 實(shí)戰(zhàn)

我們已經(jīng)介紹了一些 JavaScriptCore 中的類和協(xié)議沛膳,時(shí)候在我們的例子中使用它們了扔枫。

初始化

第一步,我們先創(chuàng)建一個(gè) JSVirtualMachine 和 JSContext 實(shí)例锹安,如果你不打算通過線程來執(zhí)行并發(fā)操作短荐,你可以用一個(gè)空的構(gòu)造函數(shù)來創(chuàng)建一個(gè) JSContext 這樣也會默認(rèn)創(chuàng)建一個(gè) JSVirtualMachine,否則的話叹哭,應(yīng)該創(chuàng)建一個(gè) JSVirtualMachine 實(shí)例忍宋,然后把它作為參數(shù)傳遞到 JSContext 的構(gòu)造函數(shù)中

方法調(diào)用執(zhí)行

有一個(gè)很棒的事情就是你擁有 WebKit 的 JavaScript 引擎,是時(shí)候加強(qiáng)一下了风罩,我喜歡iOS開發(fā)糠排,同樣也花費(fèi)大量的時(shí)間在 Node.js 和 web 開發(fā)。我經(jīng)常使用 JavaScript 庫來減少我代碼量超升,因?yàn)橛泻芏嗪苈斆鞒绦騿T相當(dāng)漂亮的完成了一些很復(fù)雜的工作入宦,現(xiàn)在我們要做的就是在我們的代碼中使用已經(jīng)存在的 JavaScript 庫。

在我們的例子中室琢,我們用 Moment.js 來做一些時(shí)間操作乾闰。我用 Playground 來完成這些。你可以建一個(gè) Playground 來跟著我一起做盈滴⌒谥遥可以使用 Bower 來安裝 Moment.js,然后拷貝 moment 目錄到工程目錄的資源文件下。如果你有疑問宽菜,可以參考我的另一篇關(guān)于 Playground的文章。

我們接下來所做的竿报,就是找到 moment.js 文件铅乡,把內(nèi)容轉(zhuǎn)為字符串,然后再注入到 JSContext 中 ;


當(dāng)你調(diào)用 JSContext 的 evaluateScript() 方法的時(shí)候烈菌,你的 JavaScript 代碼將會執(zhí)行 阵幸,在我們的例子中,我們把整個(gè) moment.js 庫作為全局對象加載到當(dāng)前的 JSContext 中芽世, 任何后來添加的腳本或者 JavaScript 對象將可以使用 moment.js 的方法挚赊。

到目前為止,這聽起來還不錯济瓢,事實(shí)上是荠割,這取決你注入到 JSContext 中的內(nèi)容,為了使 Moment.js 庫可以工作旺矾,我們應(yīng)該通過調(diào)用 evaluateScript 來調(diào)用它的構(gòu)造方法和格式化方法蔑鹦。一旦我們這么做,我們通過調(diào)用 JSContext 的 objectForKeyedSubscript 將會得到一個(gè) moment.js 的初始化對象箕宙。上邊的例子展示了怎么執(zhí)行一個(gè)方法嚎朽,調(diào)用構(gòu)造函數(shù)。這都是在背后運(yùn)行的柬帕。

例子擴(kuò)展

其實(shí)我想提供一些更深刻的例子哟忍,來讓你來思考在你代碼中使用的這些類的可能性。我們將創(chuàng)建一個(gè)小的 APP 來展示聯(lián)系人陷寝。不像真實(shí)的聯(lián)系人锅很,每次運(yùn)行的時(shí)候,我們將使用JavaScript庫 Faker 來為我們提供聯(lián)系人信息盼铁。在我們的例子中粗蔚,我們將會定義 Swift 模型,構(gòu)建可以在 JavaScript 中執(zhí)行的 Swift 方法饶火。我們也要從 JavaScript 中獲取偽造的聯(lián)系人數(shù)據(jù)鹏控。為了更加直觀,我們將使用 WKWebView 來顯示我們的聯(lián)系人肤寝。

第一步当辐,我們創(chuàng)建一個(gè)聯(lián)系人對象,列出我們想要暴露給 JavaScript 方法的 JSExports protocol 鲤看,我們用 JSContext 的 setObject 方法來確保我們的聯(lián)系人對象可以訪問 JavaScript。

接下來我們創(chuàng)建一個(gè)可以在JavaScript 中執(zhí)行的方法。我們將使用 @convention(block) 語法來把一個(gè) Swift 閉包轉(zhuǎn)為一個(gè) block蹈垢,這個(gè) block 將會變成 JavaScript 的一個(gè)含有同樣參數(shù)和返回值的方法袖裕,我們在此調(diào)用 JSContext 的 setObject 方法 把我們的方法傳遞給 JavaScript 曹抬。但是這次我們需要使用 unsafeBitCast 方法來吧一個(gè) Swift 閉包轉(zhuǎn)為 JavaScript 能正確處理的 AnyObject 類型。

下一步我們將會介紹如何在 JavaScript 中調(diào)用和使用 contact 對象急鳄。你將創(chuàng)建一個(gè) contact.js 然后把它加到 Playground 的資源文件中,我們的腳本將有一個(gè)從 Faker.js 創(chuàng)建聯(lián)系人對象的一個(gè)方法张足。它的目的是用來把一個(gè)新的聯(lián)系人添加到 Playground 的 view 上來顯示,并把這個(gè)對象返回坎藐。

這些代碼創(chuàng)建了一個(gè) JSExports protocol 和一個(gè)我們想要暴露給 JavaScript 的類對象为牍。并且我們把這類添加到了 JSContext 中使得其可以訪問 JavaScript,我們還創(chuàng)建了一個(gè)把聯(lián)系人顯示在界面上的 Swift 方法顺饮。

接下來我們把 Faker.js 加載到我們的代碼中,就像我們加載 Moment.js 一樣吟逝。同樣我們也需要添加 contact.js 赦肋,這個(gè) js 在我們的全局空間內(nèi)添加了一個(gè) createFakeContact() 方法。然后我們在本地獲得并執(zhí)行它佃乘。這有很多來來回回的調(diào)用發(fā)生在 JavaScript 與 Swift 中,這正是我想要說明的趣避。 你可以很容易的在二者之前來回的調(diào)用和傳遞程帕。

把 Faker.js 注入到 JSContext 中
把 Faker.js 注入到 JSContext 中
contact.js
contact.js

這個(gè)文件在 ContactDrawable 類中用 WKWebView 來加載 page.html 文件

page.html
page.html

為了使我們的聯(lián)系人顯示,我創(chuàng)建了一個(gè) UIView 并把它賦值給 XCPlaygroundPage.currentPage.liveView讲逛, Playground 展示了隨機(jī)創(chuàng)建的假的聯(lián)系人數(shù)據(jù)岭埠。

顯示結(jié)果
顯示結(jié)果

總結(jié)

Playground 展示了一個(gè)每次運(yùn)行都會改變的聯(lián)系人詳情列表蔚鸥。在多想一點(diǎn)许赃,我們可以適配一個(gè)用 JavaScript 創(chuàng)建并控制的 native UI,或者從服務(wù)段下載一段腳本來執(zhí)行本地的任務(wù)启盛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末技羔,一起剝皮案震驚了整個(gè)濱河市卧抗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拙绊,老刑警劉巖泳秀,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異金句,居然都是意外死亡吕嘀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進(jìn)店門趁曼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棕洋,“玉大人,你說我怎么就攤上這事掰盘。” “怎么了射众?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵晃财,是天一觀的道長典蜕。 經(jīng)常有香客問我罗洗,道長,這世上最難降的妖魔是什么轩缤? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任贩绕,我火速辦了婚禮,結(jié)果婚禮上馏鹤,老公的妹妹穿的比我還像新娘娇哆。我一直安慰自己,他們只是感情好碍讨,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布勃黍。 她就那樣靜靜地躺著,像睡著了一般溉躲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箭券,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天疑枯,我揣著相機(jī)與錄音,去河邊找鬼废亭。 笑死具钥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骂删。 我是一名探鬼主播四啰,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼柑晒,長吁一口氣:“原來是場噩夢啊……” “哼眷射!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妖碉,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤欧宜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鱼鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羹铅,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡职员,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扮授。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片专肪。...
    茶點(diǎn)故事閱讀 40,427評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嚎尤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乏梁,我是刑警寧澤关贵,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站落萎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏模暗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一碍侦、第九天 我趴在偏房一處隱蔽的房頂上張望隶糕。 院中可真熱鬧,春花似錦枚驻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至灯节,卻和暖如春绵估,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背国裳。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唯笙,地道東北人盒使。 一個(gè)月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像苞慢,于是被迫代替她去往敵國和親英妓。 傳聞我的和親對象是個(gè)殘疾皇子绍赛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本篇文章翻譯自 Medium 我承認(rèn)辑畦,我喜歡用 JavaScript 來開發(fā) web 程序。有很多庫是用 Java...
    xiao333ma閱讀 1,473評論 0 2
  • JavaScriptCore框架主要是用來實(shí)現(xiàn)iOS與H5的交互蚯妇。由于現(xiàn)在混合編程越來越多暂筝,H5的相對講多,所以研...
    水靈芳蕥閱讀 1,409評論 1 8
  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內(nèi)分享的整理务漩,大部分圖片都是直接從keynot...
    知識小集閱讀 15,254評論 11 172
  • 寫在前面 本篇文章是對我一次組內(nèi)分享的整理它褪,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,469評論 6 69
  • 注:本文copy自http://www.reibang.com/p/ac534f508fb0包吝,純屬當(dāng)筆記使用。 概...
    BookKeeping閱讀 732評論 1 3