我的视图控制器显示一个WKWebView.我安装了一个消息处理程序,这是一个很酷的网络工具包功能,允许我的代码从网页内部得到通知:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    let url = // ...
    self.wv.loadRequest(NSURLRequest(URL:url))
    self.wv.configuration.userContentController.addScriptMessageHandler(
        self, name: "dummy")
}

func userContentController(userContentController: WKUserContentController,
    didReceiveScriptMessage message: WKScriptMessage) {
        // ...
}

到目前为止一切正常,但是现在我发现我的视图控制器正在泄漏--当它应该被释放时,它并没有:

deinit {
    println("dealloc") // never called
}

似乎仅仅将我自己安装为消息处理程序就会导致保留循环,从而导致泄漏!

推荐答案

像往常一样纠正,国王星期五.事实证明,WKUserContentController retains its message handler.这在一定程度上是有意义的,因为如果其消息处理程序已不复存在,则它几乎无法向其消息处理程序发送消息.例如,这与CAAnimation保留其代理的方式平行.

但是,它也会导致保留周期,因为WKUserContentController本身正在泄漏.这本身并不重要(只有16K),但是视图控制器的保留周期和泄漏很糟糕.

我的解决方法是在WKUserContentController和消息处理程序之间插入一个蹦床对象.蹦床对象只有一个对实际消息处理程序的弱引用,因此没有保留周期.以下是蹦床对象:

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)
    }
}

现在,当我们安装消息处理程序时,我们安装的是TRAMPPOLINE对象,而不是self:

self.wv.configuration.userContentController.addScriptMessageHandler(
    LeakAvoider(delegate:self), name: "dummy")

真管用!现在叫deinit,证明没有泄漏.看起来这不应该起作用,因为我们创建了LeakAvoider对象,但从未对其进行过引用;但是请记住,WKUserContentController本身会保留它,所以没有问题.

为了完整起见,现在调用了deinit,您可以在那里卸载消息处理程序,尽管我认为这实际上没有必要:

deinit {
    println("dealloc")
    self.wv.stopLoading()
    self.wv.configuration.userContentController.removeScriptMessageHandlerForName("dummy")
}

Ios相关问答推荐

缺少预期的键:';NSPrival yCollectedDataTypes';

验证开发人员应用程序证书是否已在您的设备上验证

用于HDR显示的CI上下文选项

如何在 Swift 中存储具有关联值的协议?

如何使用自定义数据源在本地react 本机 ios 应用程序中加载 Tradingview 小部件?

Swift Vision库识别文本后如何将其赋值给 struct 体实例属性以供显示?

为什么常量属性可能在 Swift 类中被初始化两次?

如何在 SwiftUI 的 TabView 中添加底部曲线?

如何包含 Xcode 子项目标头?

SwiftUI 选项卡式视图防止滑动到下一个选项卡

在向我的 struct 添加属性后获取线程 10:EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

如何避免任何绑定空错误并更好地获取代码

Select UITableViewCell 时 UIView backgroundColor 消失

Xcode 缺少支持文件 iOS 12.2 (16E227)

故事板中的 colored颜色 与 UIColor 不匹配

如何以编程方式切换 UISegmentedControl?

Xcode / iOS模拟器:手动触发重大位置更改

我可以通过 UIAppearance 代理设置哪些属性?

从故事板导航到其他 Swift 1.2 文件时 Xcode 6.3 崩溃

UICollectionView:必须用非零布局参数初始化