寫(xiě)在前面
WebViewJavascriptBridge
启绰、ReactNative
、 JSPatch
這些 JavaScript
跟 Objective-C
交互框架都有 JavaScriptCore
的影子拾酝,所以有必要好好了解一下 JavaScriptCore
。
-
JavaScript
:下文簡(jiǎn)稱(chēng)JS
-
Objective-C
:下文簡(jiǎn)稱(chēng)OC
-
JavaScriptCore
:下文簡(jiǎn)稱(chēng)JSCore
JSCore 簡(jiǎn)介
JSCore 是 JS 引擎镶摘,通常會(huì)被叫做虛擬機(jī)湿痢,專(zhuān)門(mén)設(shè)計(jì)來(lái)解釋和執(zhí)行 JS 代碼。在 WebKit 中的結(jié)構(gòu)如下。
- WebKit Embedding API 是 Browser UI 與 WebPage 進(jìn)行交互的 API 接口
- Platform API 提供與底層驅(qū)動(dòng)的交互,如網(wǎng)絡(luò),字體渲染,影音文件解碼谅辣,渲染引擎等
- WebCore 它實(shí)現(xiàn)了對(duì)文檔的模型化熙尉,包括了 CSS, DOM, Render 等的實(shí)現(xiàn)
- JSCore 是專(zhuān)門(mén)處理 JS 腳本的引擎
另外 Google 的 Chromium(Chorme的開(kāi)源項(xiàng)目)也是使用 WebKit 联逻。WebKit 起源于 KDE 的開(kāi)源項(xiàng)目 Konqueror 的分支,由蘋(píng)果公司用于 Safari 瀏覽器检痰。其一條分支發(fā)展成為 Chorme 的內(nèi)核包归,2013 年 Google 在此基礎(chǔ)上開(kāi)發(fā)了新的 Blink 內(nèi)核。
其他 JS 解析引擎
JSCore 提供給 OC 的接口
JSCore 提供給 OC 的接口如下
OC 接口類(lèi) | 作用 |
---|---|
JSVirtualMachine | 為 JS 的運(yùn)行提供了底層資源铅歼,虛擬機(jī)是線程安全的 |
JSContext | 為 JS 提供運(yùn)行環(huán)境公壤,所有的 JS 在這個(gè)上下文中執(zhí)行换可,這里可以用來(lái)管理對(duì)象,添加方法 |
JSValue | 是 JS 和 OC 之間交互的橋梁厦幅,負(fù)責(zé)兩端對(duì)象的互相轉(zhuǎn)換 |
JSManagedValue | 可以處理內(nèi)存管理中的一些特殊情形沾鳄,它能幫助引用技術(shù)和垃圾回收這兩種內(nèi)存管理機(jī)制之間進(jìn)行正確的轉(zhuǎn)換 |
JSExport | 實(shí)現(xiàn) JSExport 協(xié)議可以開(kāi)放 OC 類(lèi)和它們的實(shí)例方法,類(lèi)方法确憨,以及屬性給 JS 調(diào)用 |
C 接口類(lèi) | 作用 |
---|---|
JSBase | 定義了 JavaScriptCore 接口文件 |
JSContextRef | 主要提供 JS 執(zhí)行所需所有資源和環(huán)境 |
JSObjectRef | 是一個(gè) JavaScript 對(duì)象,主要提供了兩部分API休弃,一部分是創(chuàng)建 JS 對(duì)象,還有一部分是給創(chuàng)建的 JS 對(duì)象添加對(duì)應(yīng)的 Callback塔猾。 |
JSValueRef | 一個(gè) JS 值篙骡,提供用 OC 的基礎(chǔ)數(shù)據(jù)類(lèi)型來(lái)創(chuàng)建 JS 的值,或者將 JS 的值轉(zhuǎn)變?yōu)?OC 的基礎(chǔ)數(shù)據(jù)類(lèi)型 |
JSStringRef | JavaScript 對(duì)象中字符串對(duì)象 |
JSStringRefCF | CFString 與 JavaScript String 相互轉(zhuǎn)化 |
我們?cè)谥饕褂玫氖?OC 接口類(lèi)丈甸,接下來(lái)依次分析 OC 接口類(lèi)糯俗。
JSVirtualMachine
一個(gè) JSVirtualMachine 的實(shí)例就是一個(gè)完整獨(dú)立的 JS 的執(zhí)行環(huán)境,為 JS 的執(zhí)行提供底層資源老虫。
這個(gè)類(lèi)主要用來(lái)做兩件事情:
- 實(shí)現(xiàn)并發(fā)的 JavaScript 執(zhí)行
- JavaScript 和 Objective-C 橋接對(duì)象的內(nèi)存管理
提供的接口也非常簡(jiǎn)單
NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSVirtualMachine : NSObject
(instancetype)init;
// 進(jìn)行內(nèi)存管理
- (void)addManagedReference:(id)object withOwner:(id)owner;
- (void)removeManagedReference:(id)object withOwner:(id)owner;
@end
每一個(gè) JSVirtualMachine 可以包含多個(gè) JS 上下文(JSContext 對(duì)象)叶骨。同一個(gè)虛擬機(jī)下不同的上下文之間可以相互傳值(JSValue對(duì)象)。
然而祈匙,每個(gè)虛擬機(jī)都是完整且獨(dú)立的,有其獨(dú)立的堆空間和垃圾回收器(Garbage Collector )天揖,GC 無(wú)法處理別的虛擬機(jī)堆中的對(duì)象夺欲,因此你不能把一個(gè)虛擬機(jī)中創(chuàng)建的值傳給另一個(gè)虛擬機(jī)。
JSContext
一個(gè) JSContext 表示了一次 JS 的執(zhí)行環(huán)境今膊。我們可以通過(guò)創(chuàng)建一個(gè) JSContext 去調(diào)用 JS 腳本些阅,訪問(wèn)一些 JS 定義的值和函數(shù),同時(shí)也提供了讓 JS 訪問(wèn) Native 對(duì)象斑唬,方法的接口市埋。
一個(gè) JSContext 對(duì)象對(duì)應(yīng)了一個(gè)全局對(duì)象。例如 Web 瀏覽器中的 JSContext 恕刘,其全局對(duì)象就是 Window 對(duì)象缤谎。在其他環(huán)境中,全局對(duì)象也承擔(dān)了類(lèi)似的角色褐着,用來(lái)區(qū)分不同的 JavaScript Context 的作用域坷澡。
同樣我們看一下接口,也是非常精簡(jiǎn)的
JS_EXPORT API_AVAILABLE(macos(10.9), ios(7.0))
@interface JSContext : NSObject
// 初始化含蓉,可以指定一個(gè)虛擬機(jī)频敛,如果沒(méi)有指定底層默認(rèn)創(chuàng)建一個(gè)
- (instancetype)init;
- (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;
// 執(zhí)行 JS 腳本项郊,返回值是 JS 中最后生成的一個(gè)值,sourceURL 認(rèn)作其源碼 URL斟赚,用作標(biāo)記
- (JSValue *)evaluateScript:(NSString *)script;
- (JSValue *)evaluateScript:(NSString *)script
withSourceURL:(NSURL *)sourceURL API_AVAILABLE(macos(10.10), ios(8.0));
// 獲取當(dāng)前執(zhí)行的 JavaScript 代碼的 context
+ (JSContext *)currentContext;
// 獲取當(dāng)前執(zhí)行的 JavaScript function
+ (JSValue *)currentCallee API_AVAILABLE(macos(10.10), ios(8.0));
// 獲取當(dāng)前執(zhí)行的 JavaScript 代碼的 this
+ (JSValue *)currentThis;
// 獲取當(dāng)前 context 回調(diào)函數(shù)的參數(shù)
+ (NSArray *)currentArguments;
// 獲取當(dāng)前 context 的全局對(duì)象
@property (readonly, strong) JSValue *globalObject;
// 用于 JavaScript 執(zhí)行異常
@property (strong) JSValue *exception;
@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);
// 獲取當(dāng)前虛擬機(jī)
@property (readonly, strong) JSVirtualMachine *virtualMachine;
// 標(biāo)記當(dāng)前 context
@property (copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0));
@end
JS 訪問(wèn) OC
{
JSContext *context = [[JSContext alloc] init];
context[@"add"] = ^(NSInteger a, NSInteger b) {
NSLog(@"a + b = %ld", a + b);
};
[context evaluateScript:@"add(1, 2)"];
}
OC 訪問(wèn) JS
{
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"function add(a, b) {return a + b}"];
JSValue *addFun = context[@"add"];
JSValue *resValue = [addFun callWithArguments:@[@1, @2]];
NSLog(@"a + b = %@", resValue);
}
JSValue
JSValue 實(shí)例是一個(gè)指向 JS 值的引用指針。我們可以使用 JSValue 類(lèi)拗军,在 OC 和 JS 的基礎(chǔ)數(shù)據(jù)類(lèi)型之間相互轉(zhuǎn)換食绿。你也可以使用這個(gè)類(lèi)去創(chuàng)建包裝了自定義類(lèi)的 Native 對(duì)象的 JS 對(duì)象,或者是那些由 Native 方法或者 Block 實(shí)現(xiàn)的 JS 函數(shù)耀销。
在 JSCore 中铲汪,JSValue 自動(dòng)做了 OC 和 JS 的類(lèi)型轉(zhuǎn)換
Objective-C type | JavaScript type |
---|---|
nil | undefined |
NSNull | null |
NSString | string |
NSNumber | number, boolean |
NSDictionary | Object object |
NSArray | Array object |
NSDate | Date object |
NSBlock | Function object |
id | Wrapper object |
Class | Constructor object |
我們繼續(xù)看一下接口狰住,非常簡(jiǎn)潔
NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSValue : NSObject
@property (readonly, strong) JSContext *context;
+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;
+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;
+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context;
+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context;
+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context;
+ (JSValue *)valueWithNewObjectInContext:(JSContext *)context;
+ (JSValue *)valueWithNewArrayInContext:(JSContext *)context;
+ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context;
+ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context;
+ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *resolve, JSValue *reject))callback API_AVAILABLE(macos(10.15), ios(13.0));
+ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));
+ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));
+ (JSValue *)valueWithNewSymbolFromDescription:(NSString *)description inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));
+ (JSValue *)valueWithNullInContext:(JSContext *)context;
+ (JSValue *)valueWithUndefinedInContext:(JSContext *)context;
- (id)toObject;
- (id)toObjectOfClass:(Class)expectedClass;
- (BOOL)toBool;
- (double)toDouble;
- (int32_t)toInt32;
- (uint32_t)toUInt32;
- (NSNumber *)toNumber;
- (NSString *)toString;
- (NSDate *)toDate;
- (NSArray *)toArray;
- (NSDictionary *)toDictionary;
@property (readonly) BOOL isUndefined;
@property (readonly) BOOL isNull;
@property (readonly) BOOL isBoolean;
@property (readonly) BOOL isNumber;
@property (readonly) BOOL isString;
@property (readonly) BOOL isObject;
@property (readonly) BOOL isArray API_AVAILABLE(macos(10.11), ios(9.0));
@property (readonly) BOOL isDate API_AVAILABLE(macos(10.11), ios(9.0));
@property (readonly) BOOL isSymbol API_AVAILABLE(macos(10.15), ios(13.0));
- (BOOL)isEqualToObject:(id)value;
- (BOOL)isEqualWithTypeCoercionToObject:(id)value;
- (BOOL)isInstanceOf:(id)value;
// 當(dāng)前 JSValue 為一個(gè)函數(shù)的時(shí)候催植,可以通過(guò)這個(gè)方法調(diào)用
- (JSValue *)callWithArguments:(NSArray *)arguments;
// 調(diào)用 JS 中的構(gòu)造函數(shù)创南,arguments 數(shù)組內(nèi)容必須是 JSValue 對(duì)象省核,以供 JS 能順利轉(zhuǎn)化
- (JSValue *)constructWithArguments:(NSArray *)arguments;
// 當(dāng)前 JSValue 對(duì)象為 JS 中的全局對(duì)象名稱(chēng)气忠,method 為全局對(duì)象的方法名稱(chēng),arguments 為參數(shù)
- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;
@end
OC 的 Block 轉(zhuǎn)換
OC 層面的 Block 是可以自動(dòng)轉(zhuǎn)換為 JS 層面的函數(shù)吨娜,JS 可以直接訪問(wèn)舌菜;但是 JS 的函數(shù) OC 確不能直接訪問(wèn),而要通過(guò) callWithArguments:
方法來(lái)調(diào)用缤骨。
OC 的 id 類(lèi)型轉(zhuǎn)換
OC 的 id 類(lèi)型傳給 JS尺借,只是一個(gè)指針,是沒(méi)法訪問(wèn)其屬性和方法的虱歪,但是 JS 回傳到 OC 的時(shí)候 OC 還是可以正常訪問(wèn)的栅表。如果需要在 JS 中,訪問(wèn) OC 對(duì)象的屬性和方法可以通過(guò) JSExport
協(xié)議來(lái)實(shí)現(xiàn)萧落,下面會(huì)詳細(xì)介紹找岖。
JSExport
實(shí)現(xiàn) JSExport 協(xié)議可以開(kāi)放 OC 類(lèi)和它們的實(shí)例方法敛滋,類(lèi)方法,以及屬性給 JS 調(diào)用
我們先看一個(gè)常規(guī)例子
@protocol OC2JSObjectExport <JSExport>
@property (nonatomic, strong) NSString *name;
- (void)callName;
+ (void)helloAtOC;
@end
@interface OC2JSObject : NSObject<OC2JSObjectExport>
@end
@implementation OC2JSObject
@synthesize name = _name;
- (void)callName
{
NSLog(@"callName:%@", self.name);
}
+ (void)helloAtOC
{
NSLog(@"helloAtOC");
}
@end
// 調(diào)用如下
{
OC2JSObject *ocObj = [OC2JSObject new];
ocObj.name = @"bob";
JSContext *context = [[JSContext alloc] init];
context[@"log"] = ^(NSString *msg){
NSLog(@"%@", msg);
};
context[@"ocObj"] = ocObj;
context[@"OC2JSObject"] = OC2JSObject.class;
// 訪問(wèn)屬性
[context evaluateScript:@"log(ocObj.name)"];
// 訪問(wèn)實(shí)例方法
[context evaluateScript:@"ocObj.callName()"];
// 訪問(wèn)類(lèi)方法
[context evaluateScript:@"OC2JSObject.helloAtOC()"];
}
如果 OC 方法有多個(gè)參數(shù)的時(shí)候
@protocol OC2JSObjectExport <JSExport>
+ (void)callVal1:(NSString *)val1 val2:(NSString *)val2;
@end
@interface OC2JSObject : NSObject<OC2JSObjectExport>
@end
@implementation OC2JSObject
+ (void)callVal1:(NSString *)val1 val2:(NSString *)val2
{
NSLog(@"val1:%@ val2:%@", val1, val2);
}
@end
// 調(diào)用如下
{
JSContext *context = [[JSContext alloc] init];
context[@"OC2JSObject"] = OC2JSObject.class;
[context evaluateScript:@"OC2JSObject.callVal1Val2('a', 'b')"];
}
多個(gè)參數(shù)的時(shí)候,轉(zhuǎn)換規(guī)則成駝峰形式
- 去掉所有的冒號(hào)
- 所有冒號(hào)后的第一個(gè)小寫(xiě)字母都會(huì)被轉(zhuǎn)為大寫(xiě)
如果不喜歡默認(rèn)的轉(zhuǎn)換規(guī)則箕昭,也可以使用 JSExportAs
來(lái)自定義轉(zhuǎn)換,比如
@protocol OC2JSObjectExport <JSExport>
JSExportAs(callVal, + (void)callVal1:(NSString *)val1 val2:(NSString *)val2);
@end
@interface OC2JSObject : NSObject<OC2JSObjectExport>
@end
@implementation OC2JSObject
+ (void)callVal1:(NSString *)val1 val2:(NSString *)val2
{
NSLog(@"val1:%@ val2:%@", val1, val2);
}
@end
// 調(diào)用如下
{
JSContext *context = [[JSContext alloc] init];
context[@"OC2JSObject"] = OC2JSObject.class;
[context evaluateScript:@"OC2JSObject.callVal('a', 'b')"];
}
小結(jié)
- 實(shí)現(xiàn) JSExport 協(xié)議可以開(kāi)放 OC 類(lèi)和它們的實(shí)例方法,類(lèi)方法述召,以及屬性給 JS 調(diào)用
- 多個(gè)參數(shù)的時(shí)候蟹地,自動(dòng)按照如下規(guī)則轉(zhuǎn)換,如果不喜歡可以通過(guò)
JSExportAs
自定義- 去掉所有的冒號(hào)
- 所有冒號(hào)后的第一個(gè)小寫(xiě)字母都會(huì)被轉(zhuǎn)為大寫(xiě)
JSCore 多線程
我們先看看一段 OC 的多線程代碼
{
__block NSInteger cnt = 0;
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 1000; i++)
{
dispatch_group_async(group, queue, ^{
cnt = cnt + 1;
});
}
dispatch_group_notify(group, queue, ^{
NSLog(@"cnt:%ld", cnt);
});
}
因?yàn)樵L問(wèn) cnt 是線程不安全的夺刑,所以最后 cnt 的值遍愿,不一定是 1000
我們?cè)倏匆欢?JS 的代碼
{
JSVirtualMachine *vm = [[JSVirtualMachine alloc] init];
JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
context[@"log"] = ^(NSString *msg) {
NSLog(@"log:%@", msg);
};
[context evaluateScript:@"var cnt = 0"];
[context evaluateScript:@"function addCnt(){cnt = cnt + 1}"];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 1000; i++)
{
dispatch_group_async(group, queue, ^{
[context evaluateScript:@"addCnt()"];
});
}
dispatch_group_notify(group, queue, ^{
[context evaluateScript:@"log(cnt)"];
});
}
這段代碼發(fā)現(xiàn)沼填,cnt 都是 1000,說(shuō)明線程安全岩饼。
JavaScriptCore 提供的 API 是線程安全的薛夜。
你可以在不同的線程中,創(chuàng)建 JSValue寞冯,用 JSContext 執(zhí)行 JS 語(yǔ)句简十,但是當(dāng)一個(gè)線程正在執(zhí)行 JS 語(yǔ)句時(shí)撬腾,其他線程想要使用這個(gè)正在執(zhí)行 JS 語(yǔ)句的 JSContext 所屬的 JSVirtualMachine 就必須得等待,等待前前一個(gè)線程執(zhí)行完胰默,才能使用這個(gè) JSVirtualMachine漓踢。
JSCore 內(nèi)存管理
目前 OC 使用的是 ARC,不能自動(dòng)解決循環(huán)引用的問(wèn)題奴迅,需要我們程序員手動(dòng)去解除循環(huán)挺据,但是 JS 使用的是 GC(垃圾回收機(jī)制),所有的引用都是強(qiáng)引用暇检,同時(shí)垃圾回收器可以幫我們解決循環(huán)引用的問(wèn)題块仆,JSCore 也是一樣的构蹬,一般來(lái)說(shuō)庄敛,大多數(shù)情況下不需要我們?nèi)ナ謩?dòng)的管理內(nèi)存蜜暑。
注意1:OC 或 JS 對(duì)象,只要有一端存在強(qiáng)引用隐绵,對(duì)象就不會(huì)釋放
比如:
@interface TCURLRequest : NSURLRequest
@end
@implementation TCURLRequest
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
{
JSContext *context = [[JSContext alloc] init];
{
TCURLRequest *request = [TCURLRequest requestWithURL:[NSURL URLWithString:@"https://www.google.com"]];
JSValue *value = [JSValue valueWithObject:request inContext:context];
[context evaluateScript:@"var ocValue"];
[context evaluateScript:@"function setValue(value){ocValue = value}"];
JSValue *setValueFun = context[@"setValue"];
[setValueFun callWithArguments:@[value]];
// 在 OC 中依许,request 這之后就要釋放了
}
JSValue *ocValue = context[@"ocValue"];
TCURLRequest *request = ocValue.toObject;
NSLog(@"%@", request);
// 在這之后才釋放的
}
注意2:循環(huán)引用缀蹄,由于 JSValue
、JSContext
蛀醉、JSVirtualMachine
都是強(qiáng)引用
比如衅码,下面的代碼就會(huì)出現(xiàn)循環(huán)引用
{
JSContext *context = [[JSContext alloc] init];
JSValue *value = [JSValue valueWithInt32:10086 inContext:context];
context[@"log"] = ^{
NSLog(@"%@", value);
};
[context evaluateScript:@"log()"];
}
我們可以使用 JSManagedValue
來(lái)解決,JSManagedValue
對(duì) JSValue
采用的是弱引用
{
JSContext *context = [[JSContext alloc] init];
JSValue *value = [JSValue valueWithInt32:10086 inContext:context];
JSManagedValue *mValue = [JSManagedValue managedValueWithValue:value];
context[@"log"] = ^{
NSLog(@"%@", mValue);
};
[context evaluateScript:@"log()"];
}
當(dāng)然垛玻,這里我們也可以使用 weak 來(lái)處理帚桩。有一種情況是 OC 引用了 JS 的屬性嘹黔,然后 JS 中也引用了 OC 的屬性,這個(gè)時(shí)候由于 JS 中沒(méi)有弱引用之說(shuō)醉锄,所以必須要用 JSManagedValue浙值。
比如:
@protocol QSExport <JSExport>
@property (nonatomic, strong) JSValue *jsValue;
@end
@interface QSObject : NSObject <QSExport>
@end
@implementation QSObject
@synthesize jsValue = _jsValue;
- (void)dealloc
{
NSLog(@"dealloc:%@", NSStringFromClass(self.class));
}
@end
@interface QSContext : JSContext
@end
@implementation QSContext
- (void)dealloc
{
NSLog(@"dealloc:%@", NSStringFromClass(self.class));
}
@end
// 調(diào)用
{
NSString *script = @"var arr = [1,2,3];\
function setObj(obj) {\
this.obj = obj;\
obj.jsValue = arr;\
}";
QSContext *context = [[QSContext alloc] init];
[context evaluateScript:script];
QSObject *obj = [[QSObject alloc] init];
[context[@"setObj"] callWithArguments:@[obj]];
}
QSObject 中的 jsValue 引用了 JS 中的 arr开呐,而且 QSObject 對(duì)象同時(shí)也被 JS 引用筐付。當(dāng)然我們可以讓 QSObject 中的 jsValue 為 weak 指針,但是由于 JS 最新在 OC 中沒(méi)有強(qiáng)引用對(duì)象沮尿,所以 weak 指針是行不通的畜疾,
比如:
@protocol QSExport <JSExport>
@property (nonatomic, weak) JSValue *jsValue;
@end
// 調(diào)用
{
NSString *script = @"var arr = [1,2,3];\
function setObj(obj) {\
this.obj = obj;\
obj.jsValue = arr;\
}";
QSContext *context = [[QSContext alloc] init];
[context evaluateScript:script];
QSObject *obj = [[QSObject alloc] init];
[context[@"setObj"] callWithArguments:@[obj]];
NSLog(@"%@", obj.jsValue); // 這里是沒(méi)有值的
}
在這種情況下印衔,我們只能使用 JSManagedValue
@protocol QSExport <JSExport>
@property (nonatomic, strong) JSValue *jsValue;
@end
@interface QSObject : NSObject <QSExport>
@property (nonatomic, strong) JSManagedValue *jsManagedValue;
@end
@implementation QSObject
@synthesize jsValue = _jsValue;
- (void)setJsValue:(JSValue *)jsValue
{
_jsManagedValue = [JSManagedValue managedValueWithValue:jsValue];
}
- (JSValue *)jsValue
{
return self.jsManagedValue.value;
}
- (void)dealloc
{
NSLog(@"dealloc:%@", NSStringFromClass(self.class));
}
@end
@interface QSContext : JSContext
@end
@implementation QSContext
- (void)dealloc
{
NSLog(@"dealloc:%@", NSStringFromClass(self.class));
}
@end
// 調(diào)用
{
NSString *script = @"var arr = [1,2,3];\
function setObj(obj) {\
this.obj = obj;\
obj.jsValue = arr;\
}";
QSContext *context = [[QSContext alloc] init];
[context evaluateScript:script];
QSObject *obj = [[QSObject alloc] init];
[context[@"setObj"] callWithArguments:@[obj]];
NSLog(@"%@", obj.jsValue);
}
這樣就能正常調(diào)用和釋放了瞎暑。