2015-06-27 19 views
7

Nie jestem pewien, czy trafiłem błąd w WebKit lub robię coś okropnie nie tak, ale nie mogę wymyślić, jak użyć WKScriptMessageHandler bez powodowania przecieku jakiejkolwiek wartości zawartej w WKScriptMessage.body.Przeciek pamięci podczas korzystania z WKScriptMessageHandler

Udało mi się zebrać minimalny projekt Mac, aby wyizolować problem, ale bez skutku.

W głównym kontrolerem Widok:

class ViewController: NSViewController { 
    var webView: WKWebView? 

    override func viewDidLoad() { 
    super.viewDidLoad() 
    let userContentController = WKUserContentController() 
    userContentController.addScriptMessageHandler(self, name: "handler") 
    let configuration = WKWebViewConfiguration() 
    configuration.userContentController = userContentController 
    webView = WKWebView(frame: CGRectZero, configuration: configuration) 
    view.addSubview(webView!) 

    let path = NSBundle.mainBundle().pathForResource("index", ofType: "html") 
    let url = NSURL(fileURLWithPath: path!)! 
    webView?.loadRequest(NSURLRequest(URL: url)) 
    } 
} 

extension ViewController: WKScriptMessageHandler { 
    func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) { 
    print(message.body) 
    } 
} 

a następnie w pliku index.html:

<html> 
    <head></head> 
    <body> 
    <script type="text/javascript"> 
     webkit.messageHandlers.handler.postMessage("Here's a random number for you: " + Math.random() * 10) 
    </script> 
    </body> 
</html> 

Kiedy uruchomić projekt, a następnie otworzyć debuggera pamięci w instrumenty, widzę po wycieku:

leak

Jeśli dodaję przycisk, który ponownie ładuje żądanie, i robi to kilkadziesiąt razy, ślad pamięci aplikacji rośnie i ulega awarii po przekroczeniu pewnego progu. Może minąć trochę czasu zanim nastąpi awaria w tym minimalnym przykładzie, ale w mojej aplikacji, w której otrzymuję kilka wiadomości na sekundę, awarie trwają mniej niż 10 sekund.

Cały projekt może być downloaded here.

Masz pojęcie o tym, co się dzieje?

Odpowiedz

5

To, co widzisz, to błąd WebKit: https://bugs.webkit.org/show_bug.cgi?id=136140. Był to fixed in WebKit trunk a while ago, ale nie wygląda na to, że został połączony z żadnymi aktualizacjami WebKit.

Można obejść to, dodając -dealloc do WKScriptMessage, który kompensuje nadmierne zatrzymanie. Może to wyglądać mniej więcej tak:

// 
// WKScriptMessage+WKScriptMessageLeakFix.m 
// TestWebkitMessages 
// 
// Created by Mark Rowe on 6/27/15. 
// Copyright © Mark Rowe. 
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions: 
// 
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software. 
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

#import <mach-o/dyld.h> 
#import <objc/runtime.h> 
#import <WebKit/WebKit.h> 

// Work around <https://webkit.org/b/136140> WKScriptMessage leaks its body 

@interface WKScriptMessage (WKScriptMessageLeakFix) 
@end 

@implementation WKScriptMessage (WKScriptMessageLeakFix) 

+ (void)load 
{ 
    // <https://webkit.org/b/136140> was fixed in WebKit trunk prior to the first v601 build being released. 
    // Enable the workaround in WebKit versions < 601. In the unlikely event that the fix is backported, this 
    // version check will need to be updated. 
    int32_t version = NSVersionOfRunTimeLibrary("WebKit"); 
    int32_t majorVersion = version >> 16; 
    if (majorVersion > 600) 
     return; 

    // Add our -dealloc to WKScriptMessage. If -[WKScriptMessage dealloc] already existed 
    // we'd need to swap implementations instead. 
    Method fixedDealloc = class_getInstanceMethod(self, @selector(fixedDealloc)); 
    IMP fixedDeallocIMP = method_getImplementation(fixedDealloc); 
    class_addMethod(self, @selector(dealloc), fixedDeallocIMP, method_getTypeEncoding(fixedDealloc)); 
} 

- (void)fixedDealloc 
{ 
    // Compensate for the over-retain in -[WKScriptMessage _initWithBody:webView:frameInfo:name:]. 
    [self.body release]; 

    // Call our WKScriptMessage's superclass -dealloc implementation. 
    [super dealloc]; 
} 

@end 

Spadek ten w pliku Objective-C w projekcie, należy ustawić flagi kompilatora dla tego pliku w celu powstrzymania -fno-objc-arc i powinien dbać o wycieku dla Ciebie.

+0

Genialny! Dzięki wielkie! –

9

Napotkano ten sam problem na SDK iOS 9.

Zauważyłem, że userContentController.addScriptMessageHandler(self, name: "handler") zachowa odniesienie do obsługi. Aby zapobiec wyciekom, po prostu usuń moduł obsługi komunikatów, gdy już go nie potrzebujesz. na przykład po zwolnieniu kontrolera wywołaj metodę czyszczenia, która wywoła removeScriptMessageHandlerForName().

Możesz rozważyć przeniesienie addScriptMessageHandler() do viewWillAppear i dodać odpowiednie wywołania removeScriptMessageHandlerForName() w viewWillDisappear.

+1

To powinna być zaakceptowana odpowiedź. –

+1

Pierwotne pytanie dotyczy wartości przekazanej jako treść wycieku wiadomości.Ta odpowiedź podkreśla fakt, że 'WKUserContentController' zachowuje zarejestrowane procedury obsługi, dopóki nie zostaną usunięte, co jest ważne, ale nie jest związane z wyciekiem treści wiadomości. To wspaniała odpowiedź, ale jest to odpowiedź na inne pytanie. – bdash

4

Masz tutaj cykl zatrzymywania. W kodzie ViewController zachowuje WKWebView, WKWebView zachowuje WKWebViewConfiguration, WKWebViewConfiguration zachowuje WKUserContentController i Twój WKUserContentController zachowuje Twój ViewController. Podobnie jak w powyższym komentarzu, musisz usunąć scriptHandler, wywołując metodę removeScriptMessageHandlerForName, przed zamknięciem kontrolera widoku.