JavaScriptCore的巨坑(JSExportAs方式綁定的本地通信)

前言

本篇分享的類型不是學(xué)習(xí)教程,并且要有一點(diǎn)JavaScriptCore基礎(chǔ)。

畢竟這一塊網(wǎng)上一大堆的學(xué)習(xí)教程,博主就沒必要班門弄斧了。

本篇的目的是分享JavaScriptCore中用JSExport協(xié)議和JSExportAs宏來進(jìn)行jsoc通信的兩個(gè)大坑。
<ol>
<li>內(nèi)存泄露</li>
<li>調(diào)用-[JSValue callWithArguments]野指針問題</li>
</ol>

block方式來進(jìn)行js和oc的通信沒這兩個(gè)大坑。

第一個(gè)坑:內(nèi)存泄露

一般綁定JSContext里的native的寫法都是self.context[@"native"] = self。但是這樣寫會產(chǎn)生內(nèi)存泄露(泄露原理就是互相持有了),這個(gè)坑隨便百度Google一下也能找到很多解決方案。目前博主的解決方案是native指定一個(gè)新的對象,然后在指定對象里實(shí)現(xiàn)JSExport協(xié)議。
貼上博主在項(xiàng)目里用到的核心代碼 :

和js通信的控制器頁面核心代碼

// 以 JSExport 協(xié)議關(guān)聯(lián) native 的方法
self.context[@"native"] = [[NMFormFlowWapNativeManager alloc] initWithDelegate:self];

NMFormFlowWapNativeManager.h

@interface NMFormFlowWapNativeManager : NSObject

- (instancetype)initWithDelegate:(id<NMFormFlowWapNativeManagerDelegate>)delegate;

@property (nonatomic,weak) id<NMFormFlowWapNativeManagerDelegate> delegate;

@end

NMFormFlowWapNativeManager.m

@import JavaScriptCore;

@protocol TestJSExport <JSExport>

JSExportAs(nativeCall, - (void)nativeCallHandleWithType:(NSString *)nativeType parameter:(NSString *)parameter jsType:(NSString *)jstype);

@end

@interface NMFormFlowWapNativeManager () <TestJSExport>

@end

@implementation NMFormFlowWapNativeManager

- (instancetype)initWithDelegate:(id<NMFormFlowWapNativeManagerDelegate>)delegate {
    if (self = [super init]) {
        self.delegate = delegate;
    }
    return self;
}

- (void)nativeCallHandleWithType:(NSString *)nativeType parameter:(NSString *)parameter jsType:(NSString *)jsType {
    NSDictionary *dicParams = [NSJSONSerialization JSONObjectWithData:[parameter dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableLeaves error:nil];
    [self.delegate nativeCallHandleWithThread:webThread type:nativeType parameter:dicParams jsType:jsType];
}

PS:代碼并不是完整的,但最核心的關(guān)鍵已經(jīng)貼上來了。順便簡單解釋一下。由于native管理的對象交給了另一個(gè),所以在管理者對象里新開了一個(gè)代理回調(diào)。方便在控制器那邊接收得到JS的事件。只要有點(diǎn)基礎(chǔ)的,一看就懂了。畢竟本篇不是學(xué)習(xí)教程,而是分享坑的。

第二個(gè)坑:-[JSValue callWithArguments]野指針問題

這個(gè)問題有點(diǎn)奇葩,JSValue的callWithArguments就是oc調(diào)用js函數(shù)所執(zhí)行的方法。那這簡單的函數(shù)怎么發(fā)生野指針問題尼。
那就是oc進(jìn)行網(wǎng)絡(luò)請求,請求完回調(diào)的時(shí)候調(diào)用JSValue的callWithArguments的方法就是產(chǎn)生野指針,而且是間接性的,有時(shí)候會有時(shí)候不會。一旦崩潰基本都直接飛去main函數(shù)了。。。。

這個(gè)問題百度Google都找了許久也沒找到類似的問題和解決方案。只是崩潰的時(shí)候,左邊的堆棧提示webThread(當(dāng)時(shí)猜測可能是線程間通信影響的此問題),然后我蒙一下切換到webView的線程里去調(diào)用callWithArguments函數(shù)試試,結(jié)果就從未發(fā)生過崩潰了。

例子:

假設(shè),h5上有一個(gè)圖片顯示和一個(gè)button,點(diǎn)擊button的時(shí)候,調(diào)用本地?cái)z像頭并且上傳圖片到服務(wù)器,上傳完之后在調(diào)用js一個(gè)函數(shù),告訴js圖片上傳成功,讓js去做對應(yīng)的邏輯。這個(gè)時(shí)候網(wǎng)絡(luò)請求完回調(diào)里的線程是主線程,調(diào)用callWithArguments的時(shí)候,就會間接性的崩潰。

解決方案

解決辦法就是回到webView的線程去調(diào)用callWithArguments就不會崩潰(因?yàn)閖s和oc綁定的函數(shù),在函數(shù)里執(zhí)行的代碼不是在主線程里執(zhí)行的)。
模擬代碼:

///假設(shè)這個(gè)函數(shù)是和js的test函數(shù)綁定的。如果監(jiān)聽到這個(gè)函數(shù)就進(jìn)行網(wǎng)絡(luò)請求或者上傳圖片等操作。
- (void)test {
  //獲取webView線程,因?yàn)閖s和oc綁定的函數(shù)里執(zhí)行的代碼不是在主線程里。
NSThread *webThread = [NSThread currentThread];
//網(wǎng)絡(luò)請求
@weakify(self);
[HTTPRequest requestGetTokenWithFinished:^(void){
  @strongify(self);

  //通知js請求完了。

 //正常情況下是直接在這里調(diào)用,但是會間接性發(fā)生野指針問題,差不多每隔四五次發(fā)生一次野指針。
  //JSValue *jsCall = self.context[@"jsCall"];
  //[jsCall callWithArguments:nil];

  //線程安全的,用此方式,筆者再也沒發(fā)生過野指針問題。
  [self performSelector:@selector(jsCall) onThread:webThread withObject:nil waitUntilDone:NO];
}];
}

- (void)jsCall {
  JSValue *jsCall = self.context[@"jsCall"];
  [jsCall callWithArguments:nil];
}

結(jié)語

總之筆者分享此文章的主要目的是第二個(gè)野指針問題,因?yàn)楣P者在Google和stackoverflow里也找了很久也找不到問題原因,然后都是蒙對的,所以才來進(jìn)行分享??赡軐τ诓欢?code>JavaScriptCore看起來有點(diǎn)困難,總之可以先了解一下。而對于js和oc的通信的業(yè)務(wù)不復(fù)雜的或者使用block進(jìn)行通信的,應(yīng)該很難遇到此問題。再者,網(wǎng)上很多學(xué)習(xí)教程基本都是推薦callWithArguments在主線程里調(diào)用,但目前筆者認(rèn)為應(yīng)該還是讓它在webView的線程里去執(zhí)行(那個(gè)野指針問題就是在主線程里執(zhí)行所發(fā)生的)。

callWithArguments野指針問題的底層實(shí)際發(fā)生原理也并不是很清楚。所以目前只能說博主是怎么解決的,但是為什么.....博主也不知其然了。有知道的方便的話也可告知一下。

而對于demo.....筆者也想寫,但對于html、js并不是很熟悉(頂多看得懂幾個(gè)標(biāo)簽)。所以....無能為力奉上demo了。

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

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

  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫。 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynot...
    知識小集閱讀 15,386評論 11 172
  • 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來的,本來有很多炫酷動效的,...
    等開會閱讀 14,727評論 6 69
  • 注:本文copy自http://m.itdecent.cn/p/ac534f508fb0,純屬當(dāng)筆記使用。 概...
    BookKeeping閱讀 791評論 1 3
  • 原創(chuàng)文章轉(zhuǎn)載請注明出處,謝謝 相信HotFix大家應(yīng)該都很熟悉了,今天主要對于最近調(diào)研的一些方案做一些總結(jié)。iOS...
    北辰明閱讀 7,772評論 6 60
  • 只需要清理SDWebImage的圖片緩存,直接用SDImageCache單例的getSize方法 2.除了圖片還有...
    lym不解釋閱讀 241評論 0 0

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