JavaScriptCore的基本用法(二)

代理設(shè)置(JS調(diào)用OC的第二種方法)

h文件

//首先寫一個協(xié)議  遵守JSExport協(xié)議
@protocol JSExportTest <JSExport>
//宏轉(zhuǎn)換下,將JS函數(shù)名稱指定為Add;
JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
@property (nonatomic, assign) NSInteger sum;

@end


//建一個對象實現(xiàn)這個協(xié)議
@interface JSTest : NSObject<JSExportTest>

@end

m文件

@implementation JSTest
@synthesize sum = _sum;
//實現(xiàn)協(xié)議方法
- (NSInteger)add:(NSInteger)a b:(NSInteger)b{
    return a + b;
}


-(void)setSum:(NSInteger)sum{
    NSLog(@"%ld",(long)sum);
    _sum = sum;
}

@end

在viewcontroller里面

    JSContext *context = [[JSContext alloc] init];
    //設(shè)置異常處理
    self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        [JSContext currentContext].exception = exception;
        NSLog(@"exception:%@",exception);
    };
    //將obj添加到context中
    scontext[@"obj"] = [][JSTest alloc]init];
    //JS里面調(diào)用obj方法,并將結(jié)果賦值給obj的sum屬性
    [context evaluateScript:@"obj.sum = obj.add(2,3)"];
    

在JS中進行調(diào)用這個對象的方法,并將結(jié)果賦值sum。唯一要注意的是OC的函數(shù)命名和JS函數(shù)命名規(guī)則問題。協(xié)議中定義的是add: b:,但是JS里面方法名字是add(a,b)。可以通過JSExportAs這個宏轉(zhuǎn)換成JS的函數(shù)名字。

異常處理

Objective-C的異常會在運行時被Xcode捕獲,而在JSContext中執(zhí)行的JavaScript如果出現(xiàn)異常,只會被JSContext捕獲并存儲在exception屬性上,而不會向外拋出。時時刻刻檢查JSContext對象的exception是否不為nil顯然是不合適,更合理的方式是給JSContext對象設(shè)置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默認值就是將傳入的exceptionValue賦給傳入的context的exception屬性:

JSContext *context = [[JSContext alloc] init];
context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
    NSLog(@"%@", exception);
    con.exception = exception;
};
 
[context evaluateScript:@"fengzhen = 66"];
 
//輸出:
//  ReferenceError: Can't find variable: fengzhen

無論是把Block傳給JSContext對象讓其變成JavaScript方法,還是把它賦給exceptionHandler屬性,在Block內(nèi)都不要直接使用其外部定義的JSContext對象或者JSValue,應(yīng)該將其當(dāng)做參數(shù)傳入到Block中,或者通過JSContext的類方法+ (JSContext *)currentContext;來獲得。否則會造成循環(huán)引用使得內(nèi)存無法被正確釋放。

內(nèi)存管理

OC使用的是ARC,JS使用的是垃圾回收機制,js的引用全都是強引用,垃圾回收機制會幫他們打破這種強引用,所以JS不存在循環(huán)引用的問題。一般情況下,OC和JS對象之間內(nèi)存管理都無需我們?nèi)リP(guān)心。不過還是有幾個注意點需要我們?nèi)チ粢庀隆?/p>

1、不要在block里面直接使用context,或者使用外部的JSValue對象。
  JSContext *context = [[JSContext alloc] init];
    //設(shè)置異常處理
    self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
    //直接這么使用是錯誤的
    //context.exception = exception;
        [JSContext currentContext].exception = exception;
        NSLog(@"exception:%@",exception);
    };
2.OC對象不要用屬性直接保存JSValue對象,因為這樣太容易造成循環(huán)引用。

下面的例子:

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
//首先寫一個協(xié)議  遵守JSExport協(xié)議
@protocol JSExportTest <JSExport>
//宏轉(zhuǎn)換下,將JS函數(shù)名稱指定為Add;
JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
@property (nonatomic, strong) JSValue *value;

@end


//建一個對象實現(xiàn)這個協(xié)議
@interface JSTest : NSObject<JSExportTest>

@end
#import "JSTest.h"

@implementation JSTest
@synthesize value = _value;
//實現(xiàn)協(xié)議方法

-(void)setValue:(JSValue *)value{
    _value = value;
}
@end

viewController里面

  JSContext *context = [[JSContext alloc]init];
    context.exceptionHandler = ^(JSContext *j, JSValue *v){
        NSLog(@"%@",j.exception);
    };
    [context evaluateScript:@"function callback(){return 'hello world'};function setObj(obj){this.obj = obj;obj.value = callback}"];
    [context[@"setObj"] callWithArguments:@[self.testObj]];

調(diào)用JS方法,進行賦值,JS對象保留了傳進來的obj,最后,JS將自己的回調(diào)callback賦值給了obj,方便obj下次回調(diào)給JS;由于JS那邊保存了obj,而且obj這邊也保留了JS的回調(diào)。這樣就形成了循環(huán)引用。
為了打破這種強引用,apple有一個JSManagedValue 的類,官方的原話:

The JSManagedValue's JavaScript value is reachable from JavaScript

The owner of the managed reference is reachable in Objective-C. Manually adding or removing the managed reference in the JSVirtualMachine determines reachability.

JSManagedValue 幫助我們保存JSValue,里面保存的JS對象必須在JS中存在,同時 JSManagedValue 的owner在OC中也存在.因此我們把代理的m文件修改如下:

-(void)setValue:(JSValue *)value{
//    由于是回掉的關(guān)系  obj保存了JS的回掉, js也保存了obj,這樣就形成了循環(huán)引用
//    JSManageValue幫助我們保存了JSValue,哪里保存的js對象在js中存在。 JSMangerValue的owner在OC中也存在。
    
    JSManagedValue *mavalue = [JSManagedValue managedValueWithValue:value];
    //建立弱引用關(guān)系
    [[[JSContext  currentContext] virtualMachine] addManagedReference:mavalue withOwner:self];
    _value = value;
}
3.不要在不同的 JSVirtualMachine 之間進行傳遞JS對象。

一個JSVirtualMachine可以運行多個context,由于都是在同一個堆內(nèi)存和同一個垃圾回收下,所以相互之間傳值是沒問題的。但是如果在不同的 JSVirtualMachine傳值,垃圾回收就不知道他們之間的關(guān)系了,可能會引起異常。

4.JavaScriptCore線程是安全的。

每個context運行的時候通過lock關(guān)聯(lián)的JSVirtualMachine。如果要進行并發(fā)操作,可以創(chuàng)建多個JSVirtualMachine實例進行操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,720評論 6 69
  • 項目中涉及OC與網(wǎng)頁的交互,查找資料時看到了JavaScriptCore.framework,就對照文章ios7 ...
    YaoYaoX閱讀 2,478評論 7 11
  • 本文由我們團隊的 糾結(jié)倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,385評論 11 172
  • 注:本文copy自http://www.itdecent.cn/p/ac534f508fb0,純屬當(dāng)筆記使用。 概...
    BookKeeping閱讀 789評論 1 3
  • 本人小白,也是最近才開始學(xué)習(xí)ReactNative,只是想記錄自己的學(xué)習(xí)歷程,供日后查閱復(fù)習(xí),文中有任何錯誤或者不...
    小唐羽鋒閱讀 1,391評論 0 51

友情鏈接更多精彩內(nèi)容