Too young too naive…
之前项目中 H5 和 原生之间互通消息一直依赖于 Cordova,还没正式用过 WKScriptMessageHandler
,前两天发现确实像网友所说的会引起循环引用,导致控制器没办法释放。🔽这篇文章写的挺清楚,我就不废话了…
【转载自】:addScriptMessageHandler 内存泄露 | The Catcher in the Rye
今天使用 addScriptMessageHandler
向 WKWebView
注入方法给 js
调用时发现有内存泄露问题。
出现问题的代码
1 2 3 4 5
| WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; WKUserContentController *userContentController = [[WKUserContentController alloc] init]; [userContentController addScriptMessageHandler:self name:@"nativeProcess"]; webViewConfiguration.userContentController= userContentController; _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) configuration:webViewConfiguration];
|
原因
调用 addScriptMessageHandler
时 userContentController
会 retain self
,而 self
又间接 retain userContentController
,形成了循环引用。
解决方案
搜了下,网上已经有解决方案了,后来在 cordova-plugin-wkwebview-engine
里也看到处理这个问题,这里记录下 3 个解决方案。
方案 1
在适当的时候调用 removeScriptMessageHandlerForName
方法。这个方案缺点时是不好找到适当的时间点,比如该 viewController
被其他地方 dismiss
这时候就不好处理。
方案 2
新建一个类来代理 self
,这样 userContentController
就 retain 这个新类的对象,新类对象只是弱引用 self
,这样循环引用就解开了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @interface CDVWKWeakScriptMessageHandler : NSObject <WKScriptMessageHandler>
@property (nonatomic, weak, readonly) id<WKScriptMessageHandler>scriptMessageHandler;
- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler;
@end @implementation CDVWKWeakScriptMessageHandler
- (instancetype)initWithScriptMessageHandler:(id<WKScriptMessageHandler>)scriptMessageHandler { self = [super init]; if (self) { _scriptMessageHandler = scriptMessageHandler; } return self; }
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { [self.scriptMessageHandler userContentController:userContentController didReceiveScriptMessage:message]; }
@end
CDVWKWeakScriptMessageHandler *weakScriptMessageHandler = [[CDVWKWeakScriptMessageHandler alloc] initWithScriptMessageHandler:self];
WKUserContentController* userContentController = [[WKUserContentController alloc] init]; [userContentController addScriptMessageHandler:weakScriptMessageHandler name:CDV_BRIDGE_NAME];
WKWebViewConfiguration* configuration = [self createConfigurationFromSettings:settings]; configuration.userContentController = userContentController;
WKWebView* wkWebView = [[WKWebView alloc] initWithFrame:self.engineWebView.frame configuration:configuration]; self.engineWebView = wkWebView;
|
方案 3
该方案跟方案 2 基本一样,不过新类集成 NSProxy
,并把所有接收到的方法都转发给 self
。该方案使用于所有类似场景,更具通用性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WeakProxy : NSProxy
+ (instancetype)weakProxy:(id)object;
@property (nonatomic, weak) id object;
@end
NS_ASSUME_NONNULL_END
#import "WeakProxy.h"
@implementation WeakProxy
+ (instancetype)weakProxy:(id)object { return [[WeakProxy alloc] initWithObject:object]; }
- (instancetype)initWithObject:(id)object { self.object = object; return self; }
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.object methodSignatureForSelector:sel]; }
- (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.object]; }
@end
|
1 2 3 4 5
| WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init]; WKUserContentController *userContentController = [[WKUserContentController alloc] init]; [userContentController addScriptMessageHandler:(id<WKScriptMessageHandler>)[WeakProxy weakProxy:self] name:@"nativeProcess"]; webViewConfiguration.userContentController= userContentController; _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) configuration:webViewConfiguration];
|
全文完
关于 WKWebView 的几篇文章:
WKWebView 基础篇
WKWebView 协议篇
WKWebView 实战篇
WKWebView Cookie 试错
WKWebView - WKScriptMessageHandler 循环引用
Demo
WebView 的 Demo