UIWebView 和 H5 交互的几个坑点
当我们建立了 JSContext 和 UIWebView 之间的关系之后。
很自然的一个场景就是我们会把 OC 的方法(block) 注入到 JS 中,让 JS 来调用。
这里有个场景:在 HTML 中,有一个按钮,点击这个按钮会modal 出来一个控制器。
然后在打开的控制器里,也有个一 WebView,里面有个按钮,点击之后,需要关闭当前这个控制器。
效果图:
![](https://img.haomeiwen.com/i2701794/d655daf952ea29f3.gif)
从第一个控制器打开第二个控制器的OC方法注入
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// 获取 WebView 的 JS 执行环境
_context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
_context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"获取 UIWebView 的 JS 执行环境失败,原因: %@",exception.toString);
};
// 注入打开控制器的方法
// self -> _context -> block -> self
__weak typeof(self) weakSelf = self;
_context[@"openVC"] = ^{
NSLog(@"JS 调用 OC 方法的线程: %@",[NSThread currentThread]);
__strong typeof(weakSelf) sself = weakSelf;
UIViewController *vc = [[SecondController alloc] init];
vc.view.backgroundColor = [UIColor orangeColor];
/**
his application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
*/
// UI 操作要放到主线程。
dispatch_async(dispatch_get_main_queue(), ^{
[sself presentViewController:vc animated:YES completion:nil];
});
};
NSLog(@"%@",@"OC 方法注入完成!");
}
上面那段代码遇到的坑点::
一、要注意循环引用。
其中 self --> jsContext -> block -> self
![](https://img.haomeiwen.com/i2701794/07de9d36ce8190aa.jpg)
解决方法也很简单:使用 weak - strong dance 打断 block 对 ViewController 的强引用。
二、OC 往 JS 中注入函数,这个函数在调用的时候,是在子线程执行的。
JS 调用 OC 方法的线程: <NSThread: 0x618000073000>{number = 5, name = (null)}
所以,如果 JS 在调用 OC 的方法涉及到 UI 界面的操作的话,要记得回到主线程。
// UI 操作要放到主线程。
dispatch_async(dispatch_get_main_queue(), ^{
[sself presentViewController:vc animated:YES completion:nil];
});
JSValue & JSContext & Block 之间的引用关系
三者都是强引用。
主要包括在:
- JSValue 引用 JSContext 里面的 JS 对象和变量。而 JS 对象和变量依赖于 JSContext。所以JSValue 对 JSContext 是强引用。
- 我们往 JS 中注入 OC 函数的 block 时候,JSContext 会拉住这个 OC 的 block。所以 JSContext 也会强引住这个 block。
- 在 block 中访问 JSValue 。等于是 block 捕获了外部变量,这个本来没什么。block 经常性的会引用外部变量。但是由于,JSContext -> block , block -> JSValue , JSValue -> JSContext。 所以就产生了循环引用了。
- 在 block 中直接访问 JSContext 也会产生强引用关系。道理也很简单:JSContext -> block ,block -> JSContext。
下面分别有代码带解决这些强引用问题。
1.在 block 中,直接访问 JSValue 导致循环引用的问题。
![](https://img.haomeiwen.com/i2701794/c1643abf6b76e55e.png)
方法一:将 JSValue 作为参数传递到 Block 中。
// JSValue -> JSContext
JSValue *value = [JSValue valueWithObject:@{@"name" : @"lisi",@"age" : @22} inContext:_context];
// JSContext -—> Block
_context[@"ocFunc"] = ^(JSValue *value) {
// Block -> JSValue
// 解决办法,把 JSValue 当做参数传递到 block 中。
NSLog(@"%@",value.toDictionary);
};
方法二:使用 weak - strong dance
// JSValue 被 block 强引用,解决方式二。
__weak typeof(value) weakValue = value;
_context[@"ocFunc3"] = ^ {
__strong typeof(weakValue) ssValue = weakValue;
NSLog(@"weak - strong dance JSValue:%@",ssValue.toDictionary);
};
2.在 block 中直接访问 JSContext 的循环引用问题
![](https://img.haomeiwen.com/i2701794/ce3f5bf47c1a7f3f.png)
解决方式一 : 使用 [JSContext currentContext] 来访问 JSContext。
_context[@"ocFunc2"] = ^{
// 解决办法1:使用 [JSContext currentContext];
[JSContext currentContext][@"doSomething"] = ^(NSString *something) {
NSLog(@"something");
};
}
解决方式二:使用 weak - strong dance 来解决
// JSContext -> block
__weak typeof(_context) weakContext = _context;
_context[@"ocFunc2"] = ^{
// 解决方法二,使用 weak-strong dance
__strong typeof(weakContext) ssContext = weakContext;
ssContext[@"doSomething2"] = ^(NSString *someThing) {
NSLog(@"%@",someThing);
};
};
最后总结:
- 由于 JSValue 指向的 JS 值依赖于 JSContext 上下文环境。所以 JSValue 天生的强引用了 JSContext。
- JSContext 注入 OC 的方法到 JS。JS 需要调用这个 OC 的 block。所以 JSContext 天生的会强引用这个 block。
- 在 block 中直接用捕获的方式使用 JSValue 导致循环引用是因为:JSValue 强引用了 JSContext。
- 在 block 中直接使用 JSContext,导致了循环引用就很普通了:两个对象都相互引用了对方。
网友评论