Corretto come al solito, King Friday. Risulta che il WKUserContentController conserva il suo gestore di messaggi. Questo ha un certo senso, dal momento che potrebbe difficilmente inviare un messaggio al suo gestore di messaggi se il suo gestore di messaggi aveva cessato di esistere. Ad esempio, è parallelo al modo in cui un CAAnimation mantiene il proprio delegato.
Tuttavia, causa anche un ciclo di conservazione, poiché il WKUserContentController perde. Non importa molto da solo (è solo 16K), ma il ciclo di conservazione e la perdita del controller di visualizzazione sono pessimi.
La mia soluzione è interporre un oggetto trampolino tra WKUserContentController e il gestore di messaggi. L'oggetto trampolino ha solo un debole riferimento al vero gestore del messaggio, quindi non c'è un ciclo di conservazione. Ecco l'oggetto trampolino:
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)
}
}
Ora, quando installiamo il gestore di messaggi, installiamo l'oggetto trampolino invece di self
:
self.wv.configuration.userContentController.addScriptMessageHandler(
LeakAvoider(delegate:self), name: "dummy")
Funziona! Ora viene chiamato deinit
, a dimostrazione dell'assenza di perdite. Sembra che questo non dovrebbe funzionare, perché abbiamo creato il nostro oggetto LeakAvoider e non abbiamo mai avuto un riferimento ad esso; ma ricorda, lo stesso WKUserContentController lo sta mantenendo, quindi non ci sono problemi.
Per completezza, ora che deinit
si chiama, è possibile disinstallare il gestore di messaggi lì, anche se non credo che questo è effettivamente necessario:
deinit {
println("dealloc")
self.wv.stopLoading()
self.wv.configuration.userContentController.removeScriptMessageHandlerForName("dummy")
}
Sorpreso upvote non superiore. Grande aiuto – Nick
un'anima gentile può tradurre questo in codici equivalenti oggettivec? – mkto
@mkto - Pubblicato una versione obj-c dell'implementazione. – johan