2014-01-16 9 views
19

Ich musste HTML-Text in meiner iOS App anzeigen lassen. Ich habe beschlossen, dass ich die eingebaute Methode auf NSAttributedString, initWithData:options:documentAttributes:error: verwenden werde. Das eigentliche Parsen funktioniert hervorragend, allerdings scheint mir ein sehr merkwürdiger Bug aufgetaucht zu sein, der sich nur dann zu manifestieren scheint, wenn ich den Debugger angehängt habe.Warum dauert der erste Aufruf von NSAttributedString mit einer HTML-Zeichenfolge mehr als 100 Mal länger als nachfolgende Aufrufe?

Das erste Mal, dass diese Methode aufgerufen wird, dauert es knapp unter 1 Sekunde auf meinem iPhone 5S mit iOS 7.0.4 und etwa 1,5 Sekunden auf einem iPod Touch der 5. Generation. Die Eigenart manifestiert sich auch am Simulator, ist aber aufgrund der schieren Geschwindigkeit des Simulators deutlich weniger auffällig.

Nachfolgende Anrufe dauern nur etwa 10-50 ms, was deutlich schneller ist als der erste Anruf.

Dies scheint nicht im Zusammenhang mit der Zwischenspeicherung der Eingabezeichenfolge zu stehen, da ich es mit mehreren Eingabezeichenfolgen in meiner "echten" Anwendung getestet habe.

Wenn ich jedoch das Programm ohne den Debugger ausführen, wird es wie erwartet ausgeführt und dauert etwa 10-20ms, was ich erwarten HTML-Parsing zu nehmen.

Hier ist der relevante Abschnitt des Codes:

-(void) benchmarkMe:(id)sender { 
    NSData *data = [testString dataUsingEncoding:NSUTF8StringEncoding]; 

    NSTimeInterval startTime = [[NSDate date] timeIntervalSinceReferenceDate]; 

    // So the complier doesn't keep complaining at me. 
    __attribute__((unused)) 
    NSAttributedString *parsed = [[NSAttributedString alloc] initWithData:data 
                    options:@{ 
                     NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, 
                     NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding) 
                    } 
                 documentAttributes:nil 
                    error:nil]; 

    NSTimeInterval endTime = [[NSDate date] timeIntervalSinceReferenceDate]; 

    NSString *message = [NSString stringWithFormat:@"Took %lf seconds.", endTime - startTime]; 

    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Benchmark complete!" 
                 message:message 
                 delegate:nil 
               cancelButtonTitle:@"Ok" 
               otherButtonTitles:nil]; 
    [alertView show]; 
} 

Hinweis: Ein volles Projekt arbeiten, diese Fehler demonstriert hier verfügbar:
https://github.com/richardjrossiii/NSAttributedStringHTMLBug

Bin ich verrückt? Gibt es etwas, das mir hier fehlt? 1 Sekunde ist unglaublich viel Zeit, wenn ich versuche, meine App für die Leistung zu optimieren.

Meine aktuelle Lösung ist es, eine "Dummy" Zeichenfolge beim Start der Anwendung zu analysieren, aber das scheint wie eine unglaublich hacky Workaround.

Antwort

21

Das ist eine wirklich gute Frage. Es stellt sich heraus, dass es (zumindest für mich) beim ersten Aufruf der Methode immer langsamer ist, egal ob der Debugger angehängt ist oder nicht. Hier ist der Grund: Wenn Sie zum ersten Mal eine HTML-Zeichenfolge analysieren, lädt iOS eine ganze JavaScript-Engine und ein WebKit in den Speicher. Beobachten:

Das erste Mal, wenn wir die Methode ausgeführt (vor der Zeichenfolgenanalyse) nur 3 Threads existieren:

screenshot 1

nach dem String syntaktisch analysiert wird, haben wir 11 Fäden:

screenshot 2

Jetzt das nächste Mal führen wir die Methode, die meisten von diese webbezogenen Fäden sind noch vorhanden:

screenshot 3

Das erklärt, warum es langsam ist das erste Mal, und schnell danach.

+1

Warum in der Welt würde JavaScriptCore verwenden? Scheint wie eine schreckliche Idee ... Wie auch immer, nachdem ich einige eigene Instrumente erstellt habe, stimmt es mit deinen Entdeckungen überein. –

+1

@RichardJ.RossIII Ich habe keine Ahnung, warum es JSC lädt. Ich verstehe, dass es WebKit lädt, weil das Parsen einer HTML-Zeichenfolge teuer ist: Es muss eine DOM-Struktur erstellt, geparst und (möglicherweise sogar) CSS-Stile angewendet werden. Selbst wenn nur 'bold' geparst wird, muss eine ganze HTML-Parsing-Maschine gestartet werden. –

+2

Unglaublich interessante Beobachtungen hier Jungs. Schöne Untersuchung – mattsven