前言
本篇分享的類(lèi)型不是學(xué)習(xí)教程鳍侣,并且要有一點(diǎn)JavaScriptCore
基礎(chǔ)禁筏。
畢竟這一塊網(wǎng)上一大堆的學(xué)習(xí)教程尽楔,博主就沒(méi)必要班門(mén)弄斧了投储。
本篇的目的是分享JavaScriptCore
中用JSExport
協(xié)議和JSExportAs
宏來(lái)進(jìn)行js
和oc
通信的兩個(gè)大坑。
<ol>
<li>內(nèi)存泄露</li>
<li>調(diào)用-[JSValue callWithArguments]野指針問(wèn)題</li>
</ol>
用
block
方式來(lái)進(jìn)行js和oc的通信沒(méi)這兩個(gè)大坑阔馋。
第一個(gè)坑:內(nèi)存泄露
一般綁定JSContext
里的native
的寫(xiě)法都是self.context[@"native"] = self
玛荞。但是這樣寫(xiě)會(huì)產(chǎn)生內(nèi)存泄露(泄露原理就是互相持有了),這個(gè)坑隨便百度Google一下也能找到很多解決方案呕寝。目前博主的解決方案是native指定一個(gè)新的對(duì)象勋眯,然后在指定對(duì)象里實(shí)現(xiàn)JSExport協(xié)議。
貼上博主在項(xiàng)目里用到的核心代碼 :
和js通信的控制器頁(yè)面核心代碼
// 以 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)貼上來(lái)了客蹋。順便簡(jiǎn)單解釋一下塞蹭。由于
native
管理的對(duì)象交給了另一個(gè),所以在管理者對(duì)象里新開(kāi)了一個(gè)代理回調(diào)讶坯。方便在控制器那邊接收得到JS
的事件番电。只要有點(diǎn)基礎(chǔ)的,一看就懂了辆琅。畢竟本篇不是學(xué)習(xí)教程漱办,而是分享坑的。
第二個(gè)坑:-[JSValue callWithArguments]野指針問(wèn)題
這個(gè)問(wèn)題有點(diǎn)奇葩婉烟,JSValue的callWithArguments就是oc調(diào)用js函數(shù)所執(zhí)行的方法娩井。那這簡(jiǎn)單的函數(shù)怎么發(fā)生野指針問(wèn)題尼。
那就是oc進(jìn)行網(wǎng)絡(luò)請(qǐng)求似袁,請(qǐng)求完回調(diào)的時(shí)候調(diào)用JSValue的callWithArguments的方法就是產(chǎn)生野指針洞辣,而且是間接性的,有時(shí)候會(huì)有時(shí)候不會(huì)昙衅。一旦崩潰基本都直接飛去main
函數(shù)了扬霜。。绒尊。畜挥。
這個(gè)問(wèn)題百度Google都找了許久也沒(méi)找到類(lèi)似的問(wèn)題和解決方案。只是崩潰的時(shí)候婴谱,左邊的堆棧提示
webThread
(當(dāng)時(shí)猜測(cè)可能是線(xiàn)程間通信影響的此問(wèn)題)蟹但,然后我蒙一下切換到webView
的線(xiàn)程里去調(diào)用callWithArguments
函數(shù)試試,結(jié)果就從未發(fā)生過(guò)崩潰了谭羔。
例子:
假設(shè)华糖,h5上有一個(gè)圖片顯示和一個(gè)button
,點(diǎn)擊button的時(shí)候瘟裸,調(diào)用本地?cái)z像頭并且上傳圖片到服務(wù)器客叉,上傳完之后在調(diào)用js
一個(gè)函數(shù),告訴js圖片上傳成功话告,讓js去做對(duì)應(yīng)的邏輯兼搏。這個(gè)時(shí)候網(wǎng)絡(luò)請(qǐng)求完回調(diào)里的線(xiàn)程是主線(xiàn)程,調(diào)用callWithArguments的時(shí)候沙郭,就會(huì)間接性的崩潰佛呻。
解決方案
解決辦法就是回到webView
的線(xiàn)程去調(diào)用callWithArguments
就不會(huì)崩潰(因?yàn)閖s和oc綁定的函數(shù),在函數(shù)里執(zhí)行的代碼不是在主線(xiàn)程里執(zhí)行的)病线。
模擬代碼:
///假設(shè)這個(gè)函數(shù)是和js的test函數(shù)綁定的吓著。如果監(jiān)聽(tīng)到這個(gè)函數(shù)就進(jìn)行網(wǎng)絡(luò)請(qǐng)求或者上傳圖片等操作鲤嫡。
- (void)test {
//獲取webView線(xiàn)程,因?yàn)閖s和oc綁定的函數(shù)里執(zhí)行的代碼不是在主線(xiàn)程里绑莺。
NSThread *webThread = [NSThread currentThread];
//網(wǎng)絡(luò)請(qǐng)求
@weakify(self);
[HTTPRequest requestGetTokenWithFinished:^(void){
@strongify(self);
//通知js請(qǐng)求完了暖眼。
//正常情況下是直接在這里調(diào)用,但是會(huì)間接性發(fā)生野指針問(wèn)題纺裁,差不多每隔四五次發(fā)生一次野指針诫肠。
//JSValue *jsCall = self.context[@"jsCall"];
//[jsCall callWithArguments:nil];
//線(xiàn)程安全的,用此方式对扶,筆者再也沒(méi)發(fā)生過(guò)野指針問(wèn)題区赵。
[self performSelector:@selector(jsCall) onThread:webThread withObject:nil waitUntilDone:NO];
}];
}
- (void)jsCall {
JSValue *jsCall = self.context[@"jsCall"];
[jsCall callWithArguments:nil];
}
結(jié)語(yǔ)
總之筆者分享此文章的主要目的是第二個(gè)野指針問(wèn)題,因?yàn)楣P者在Google和stackoverflow里也找了很久也找不到問(wèn)題原因浪南,然后都是蒙對(duì)的,所以才來(lái)進(jìn)行分享漱受÷缭洌可能對(duì)于不懂JavaScriptCore
看起來(lái)有點(diǎn)困難,總之可以先了解一下昂羡。而對(duì)于js和oc的通信的業(yè)務(wù)不復(fù)雜的或者使用block進(jìn)行通信的絮记,應(yīng)該很難遇到此問(wèn)題。再者虐先,網(wǎng)上很多學(xué)習(xí)教程基本都是推薦callWithArguments
在主線(xiàn)程里調(diào)用怨愤,但目前筆者認(rèn)為應(yīng)該還是讓它在webView的線(xiàn)程里去執(zhí)行(那個(gè)野指針問(wèn)題就是在主線(xiàn)程里執(zhí)行所發(fā)生的)。
而callWithArguments
野指針問(wèn)題的底層實(shí)際發(fā)生原理也并不是很清楚蛹批。所以目前只能說(shuō)博主是怎么解決的撰洗,但是為什么.....博主也不知其然了。有知道的方便的話(huà)也可告知一下腐芍。
而對(duì)于demo
.....筆者也想寫(xiě)差导,但對(duì)于html、js
并不是很熟悉(頂多看得懂幾個(gè)標(biāo)簽)猪勇。所以....無(wú)能為力奉上demo了设褐。