前言
本篇分享的類型不是學習教程,并且要有一點JavaScriptCore
基礎。
畢竟這一塊網上一大堆的學習教程,博主就沒必要班門弄斧了。
本篇的目的是分享JavaScriptCore
中用JSExport
協議和JSExportAs
宏來進行js
和oc
通信的兩個大坑。
<ol>
<li>內存泄露</li>
<li>調用-[JSValue callWithArguments]野指針問題</li>
</ol>
用
block
方式來進行js和oc的通信沒這兩個大坑。
第一個坑:內存泄露
一般綁定JSContext
里的native
的寫法都是self.context[@"native"] = self
。但是這樣寫會產生內存泄露(泄露原理就是互相持有了),這個坑隨便百度Google一下也能找到很多解決方案。目前博主的解決方案是native指定一個新的對象,然后在指定對象里實現JSExport協議。
貼上博主在項目里用到的核心代碼 :
和js通信的控制器頁面核心代碼
// 以 JSExport 協議關聯 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:代碼并不是完整的,但最核心的關鍵已經貼上來了。順便簡單解釋一下。由于
native
管理的對象交給了另一個,所以在管理者對象里新開了一個代理回調。方便在控制器那邊接收得到JS
的事件。只要有點基礎的,一看就懂了。畢竟本篇不是學習教程,而是分享坑的。
第二個坑:-[JSValue callWithArguments]野指針問題
這個問題有點奇葩,JSValue的callWithArguments就是oc調用js函數所執行的方法。那這簡單的函數怎么發生野指針問題尼。
那就是oc進行網絡請求,請求完回調的時候調用JSValue的callWithArguments的方法就是產生野指針,而且是間接性的,有時候會有時候不會。一旦崩潰基本都直接飛去main
函數了。。。。
這個問題百度Google都找了許久也沒找到類似的問題和解決方案。只是崩潰的時候,左邊的堆棧提示
webThread
(當時猜測可能是線程間通信影響的此問題),然后我蒙一下切換到webView
的線程里去調用callWithArguments
函數試試,結果就從未發生過崩潰了。
例子:
假設,h5上有一個圖片顯示和一個button
,點擊button的時候,調用本地攝像頭并且上傳圖片到服務器,上傳完之后在調用js
一個函數,告訴js圖片上傳成功,讓js去做對應的邏輯。這個時候網絡請求完回調里的線程是主線程,調用callWithArguments的時候,就會間接性的崩潰。
解決方案
解決辦法就是回到webView
的線程去調用callWithArguments
就不會崩潰(因為js和oc綁定的函數,在函數里執行的代碼不是在主線程里執行的)。
模擬代碼:
///假設這個函數是和js的test函數綁定的。如果監聽到這個函數就進行網絡請求或者上傳圖片等操作。
- (void)test {
//獲取webView線程,因為js和oc綁定的函數里執行的代碼不是在主線程里。
NSThread *webThread = [NSThread currentThread];
//網絡請求
@weakify(self);
[HTTPRequest requestGetTokenWithFinished:^(void){
@strongify(self);
//通知js請求完了。
//正常情況下是直接在這里調用,但是會間接性發生野指針問題,差不多每隔四五次發生一次野指針。
//JSValue *jsCall = self.context[@"jsCall"];
//[jsCall callWithArguments:nil];
//線程安全的,用此方式,筆者再也沒發生過野指針問題。
[self performSelector:@selector(jsCall) onThread:webThread withObject:nil waitUntilDone:NO];
}];
}
- (void)jsCall {
JSValue *jsCall = self.context[@"jsCall"];
[jsCall callWithArguments:nil];
}
結語
總之筆者分享此文章的主要目的是第二個野指針問題,因為筆者在Google和stackoverflow里也找了很久也找不到問題原因,然后都是蒙對的,所以才來進行分享。可能對于不懂JavaScriptCore
看起來有點困難,總之可以先了解一下。而對于js和oc的通信的業務不復雜的或者使用block進行通信的,應該很難遇到此問題。再者,網上很多學習教程基本都是推薦callWithArguments
在主線程里調用,但目前筆者認為應該還是讓它在webView的線程里去執行(那個野指針問題就是在主線程里執行所發生的)。
而callWithArguments
野指針問題的底層實際發生原理也并不是很清楚。所以目前只能說博主是怎么解決的,但是為什么.....博主也不知其然了。有知道的方便的話也可告知一下。
而對于demo
.....筆者也想寫,但對于html、js
并不是很熟悉(頂多看得懂幾個標簽)。所以....無能為力奉上demo了。