uiwebview打包html5,UIWebView 和 H5 交互的几个坑点

UIWebView 和 H5 交互的几个坑点

当我们建立了 JSContext 和 UIWebView 之间的关系之后。

很自然的一个场景就是我们会把 OC 的方法(block) 注入到 JS 中,让 JS 来调用。

这里有个场景:在 HTML 中,有一个按钮,点击这个按钮会modal 出来一个控制器。

然后在打开的控制器里,也有个一 WebView,里面有个按钮,点击之后,需要关闭当前这个控制器。

效果图:

72f522ca26cb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

效果图

从第一个控制器打开第二个控制器的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

72f522ca26cb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

循环引用关系

解决方法也很简单:使用 weak - strong dance 打断 block 对 ViewController 的强引用。

二、OC 往 JS 中注入函数,这个函数在调用的时候,是在子线程执行的。

JS 调用 OC 方法的线程: {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 导致循环引用的问题。

72f522ca26cb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

在 block 中直接访问 JSValue 导致的循环引用

方法一:将 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 的循环引用问题

72f522ca26cb?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

在 block 中直接访问 JSContext 导致的循环引用

解决方式一 : 使用 [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,导致了循环引用就很普通了:两个对象都相互引用了对方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值