[iOS, Objective-c] WKWebView ScriptMessageHandler Memory Leak
addScriptMessageHandler
@interface WebViewController() <WKScriptMessageHandler>
...
@end
@implementation WebViewController
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *sConfiguration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *sContentController = [[WKUserContentController alloc] init];
[sContentController addScriptMessageHandler:self name:@"message"];
[sConfiguration setUserContentController:sContentController];
self.webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:sConfiguration];
...
}
...
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message
{
...
}
@end
Memory leak
WKUserContentController *sContentController = [[WKUserContentController alloc] init];
[sContentController addScriptMessageHandler:self name:@"message"];
- the
WKUserContentController
holds a strong reference to the message handlerself
- so it causes a
retain cycle
Solution #1
- remove the message handler
self.webview.configuration.userContentController.removeScriptMessageHandlerForName("message")
Solution #2
-
create a object to solve the retain cycle
-
objective-c
@interface WeakScriptMessageDelegate : NSObject<WKScriptMessageHandler>
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
@end
@implementation WeakScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate
{
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
@end
// add message hanlder
WKUserContentController *sContentController = [[WKUserContentController alloc] init];
[sContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"message"];
- swift
class LeakAvoider : NSObject, WKScriptMessageHandler {
weak var delegate : WKScriptMessageHandler?
init(delegate:WKScriptMessageHandler) {
self.delegate = delegate
super.init()
}
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
self.delegate?.userContentController(userContentController, didReceiveScriptMessage: message)
}
}
// add message hanlder
self.webview.configuration.userContentController.addScriptMessageHandler(LeakAvoider(delegate:self), name: "message")
Leave a comment