Wie üblich, König Freitag. Es stellt sich heraus, dass der WKUserContentController seinen Nachrichtenhandler beibehält. Dies macht eine gewisse Menge Sinn, da es kaum eine Nachricht an seinen Nachrichtenhandler senden könnte, wenn sein Nachrichtenhandler aufgehört hätte zu existieren. Es ist parallel zu der Art und Weise, wie eine CAAnimation beispielsweise ihren Delegaten behält.
Es verursacht jedoch auch einen Retain-Zyklus, da der WKUserContentController selbst undicht ist. Das ist nicht sonderlich wichtig (es sind nur 16K), aber der Retain-Zyklus und das Leck des View-Controllers sind schlecht.
Meine Problemumgehung besteht darin, ein Trampoline-Objekt zwischen dem WKUserContentController und dem Nachrichtenhandler einzufügen. Das Trampoline-Objekt hat nur eine schwache Referenz auf den echten Nachrichtenhandler, so dass es keinen Retain-Zyklus gibt. Hier ist das Trampolin Objekt:
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)
}
}
Nun, wenn wir die Nachrichten-Handler installieren wir das Trampolin Objekt statt self
installieren:
self.wv.configuration.userContentController.addScriptMessageHandler(
LeakAvoider(delegate:self), name: "dummy")
Es funktioniert! Jetzt wird deinit
aufgerufen, was beweist, dass kein Leck vorhanden ist. Es sieht so aus, als ob dies nicht funktionieren sollte, da wir unser LeakAvoider-Objekt erstellt haben und nie einen Verweis darauf hatten; aber denken Sie daran, der WKUserContentController selbst behält es, so dass es kein Problem gibt.
Für Vollständigkeit, jetzt, wo deinit
genannt wird, können Sie die Nachrichten-Handler es deinstallieren, obwohl ich nicht glaube, dies tatsächlich erforderlich ist:
deinit {
println("dealloc")
self.wv.stopLoading()
self.wv.configuration.userContentController.removeScriptMessageHandlerForName("dummy")
}
überrascht nicht höher upvote. Große Hilfe. – Nick
kann irgendeine Art Seele dieses zu den objektiven äquivalenten Codes übersetzen? – mkto
@mkto - Posted eine obj-c-Version der Implementierung. – johan