2013-12-22 20 views
13

Ich zeige verschiedene Arten von Inhalten in einem tableview und berechnen Sie die Höhe jeder Zelle mit verschiedenen benutzerdefinierten Methoden, in heightForRowAtIndexPath.NSMutableAttributedString initWithData: verursacht EXC_BAD_ACCESS bei Rotation

Eine dieser benutzerdefinierten Methoden impliziert das Konvertieren einiger HTML in einem NSMutableAttributedString und das Berechnen der Höhe dieses NSMutableAttributedString. Für die HTML-Konvertierung verwende ich die neue initWithData: Methode.

Alles funktioniert einwandfrei, außer wenn ich den Bildschirm rotiere => Ich habe jedes Mal einen exc_bad_access.

Mit Hilfe von Instrumenten/Zombies konnte ich den Fehler lokalisieren, und tatsächlich ist es das initWithData:.

(Wenn ich diese Methode entfernen und erstellen Sie eine "einfache" NSMutableAttributedString mit , kann ich die Orientierung so oft ändern, wie ich will, keine crash mehr).

Irgendeine Idee warum?

(By the way, mein Projekt ARC verwenden)


Instrument/Zombie-Screenshot: enter image description here


Eigene Methode in heightForRowAtIndexPath genannt:

< UtilitiesForFrontEndUI heightForFacebookAttributedText:>

+(CGFloat)heightForFacebookAttributedText:(NSString *)attributedText withWidth:(CGFloat)width 
{ 
    NSAttributedString *formatedText = [self formatRawFacebookContentForFrontEndRichTextContents:attributedText]; 
    CGRect rect= [formatedText boundingRectWithSize:CGSizeMake(width, 1000) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil]; 
    return ceilf(rect.size.height); 
} 

Benutzerdefinierte Methode, um die initWithData für html NSMutableAttributedString Konvertierung mit:

< UtilitiesForFrontEndUI formatRawFacebookContentForFrontEndRichTextContents:>

+(NSAttributedString *)formatRawFacebookContentForFrontEndRichTextContents:(NSString *)stringToFormat 
{ 
    // THIS GENERATE EXC_BAD_ACCESS ON DEVICE ROTATION (WORKS IF NO ROTATION) 
    NSData *dataContent = [stringToFormat dataUsingEncoding:NSUTF8StringEncoding]; 
    NSMutableAttributedString *richTxtContent = [[NSMutableAttributedString alloc] initWithData:dataContent options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil]; 

    NSRange myRange; 
    myRange.location = 0; 
    myRange.length = richTxtContent.length; 

    [richTxtContent addAttributes:[self commonAttributesForFrontEndRichText] range:myRange]; 

    return richTxtContent; 
} 

Wenn ich ersetzen initWithData durch eine einfache initWithString nicht mehr EXC_BAD_ACCESS

+(NSAttributedString *)formatRawFacebookContentForFrontEndRichTextContents:(NSString *)stringToFormat 
{ 
    // THIS WORKS (NO MORE ROTATION CRASH) 
    NSMutableAttributedString *richTxtContent = [[NSMutableAttributedString alloc]initWithString:stringToFormat]; 

    NSRange myRange; 
    myRange.location = 0; 
    myRange.length = richTxtContent.length; 

    [richTxtContent addAttributes:[self commonAttributesForFrontEndRichText] range:myRange]; 

    return richTxtContent; 
} 

Antwort

11

Ich habe eine ähnliche Situation in meiner App passiert.

[NSMutableAttributedString initWithData:] kann sehr lange dauern, insbesondere bei großen Eingängen. Meine Vermutung ist, während dieser Aufruf ausgeführt wird, muss der UIKit-Rotationsbehandlungscode ausgeführt werden, aber da Ihr Hauptthread auf dem initWithData: call feststeckt, gehen die Dinge ein wenig aus dem Ruder.

Versuchen Sie, den Parsing Anruf weg von dem Haupt-Thread zu bewegen, so dass sie es nicht blockieren:

+(NSAttributedString *)formatRawFacebookContentForFrontEndRichTextContents:(NSString *)stringToFormat completion:(void (^)(NSAttributedString *))completion 
    { 
      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
       NSData *dataContent = [stringToFormat dataUsingEncoding:NSUTF8StringEncoding]; 
       NSMutableAttributedString *richTxtContent = [[NSMutableAttributedString alloc] initWithData:dataContent options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil]; 

       NSRange myRange; 
       myRange.location = 0; 
       myRange.length = richTxtContent.length; 

       [richTxtContent addAttributes:[self commonAttributesForFrontEndRichText] range:myRange]; 

       dispatch_async(dispatch_get_main_queue(), ^{ 
         if (completion) 
          completion(richTxtContent); 
       }) 
      }); 
    } 

Es ist auch möglich, dass, während die Drehung geschieht, ein Objekt zu Ihrer Methode verwendet wird freigegeben, verursacht die EXC_BAD_ACCESS. Sie müssen Debugging auf die - (void)dealloc und Rotation Methoden durchführen, um zu sehen, was vor sich geht.

Ein weiteres Stück der entsprechenden Dokumentation sind folgende:

Multicore- Überlegungen: Da OS X v10.4, NSAttributedString hat verwendet WebKit für alle Import (aber nicht für den Export) von HTML-Dokumenten. Da das Laden des WebKit-Dokuments nicht Thread-sicher ist, war dies nicht sicher für Hintergrundthreads zu verwenden. Für Anwendungen, die unter OS X v10.5 und höher verknüpft sind, wenn NSAttributedString HTML-Dokumente auf einen beliebigen aber Hauptthread importiert, wird die Verwendung von WebKit über performSelectorOnMainThread: withObject: waitUntilDone: an den Haupt-Thread übertragen. Dieser macht die Operation thread sicher, aber es erfordert, dass der Hauptthread die Ausführungsschleife in einem der allgemeinen Modi ausführt. Dieses Verhalten kann überschrieben werden, indem der Wert des Standardbenutzerstandards NSRunWebKitOnAppKitThread auf entweder YES gesetzt wird (um das neue Verhalten unabhängig von der Verknüpfung zu erhalten) oder NO (um das alte Verhalten unabhängig von Verknüpfung zu erhalten).

Source

+0

Danke, sind Sie wahrscheinlich Recht (so dass ich die Antwort akzeptiert). Ich ging den TTTAttributedLabel Weg, wie ich auch klickbare "Verbindungen" innerhalb dieses attributedString haben wollte. Es funktioniert perfekt (nach einigen harten Zeiten lernen, wie man es richtig verwendet) = sehr schnell + flexibel und einfach zu integrieren/verwalten + Delegieren Methoden – macbeb

+2

Ich würde hinzufügen, dass Apple ausdrücklich sagt, verwenden Sie nicht die UIKit-Version auf einem Hintergrund-Thread. Ich habe das mit performSelectorOnMainThread behoben: object: waitUntilDone: und es funktioniert wie ein Zauber. –

+0

@Jesse Naugher Ich weiß, das ist etwas alt, aber wie hast du 'performSelectorOnMainThread: object: waitUntilDone:' in Verbindung mit dieser Antwort, die Sie kommentiert? In ein ähnliches Problem geraten und nach einer Lösung suchen. –

Verwandte Themen