14

我在 a 中显示不同类型的内容,tableview并使用不同的自定义方法计算每个单元格的高度,在heightForRowAtIndexPath.

其中一种自定义方法意味着在 中转换一些 html NSMutableAttributedString,然后计算 this 的高度NSMutableAttributedString
对于 html 转换,我使用新initWithData:方法。

一切正常,除非我旋转屏幕 => 我每次都有一个 exc_bad_access。

使用 Instruments / Zombies,我已经能够找到错误,实际上就是这个initWithData:.

(当我删除此方法并使用 创建“简单”NSMutableAttributedStringinitWithString,我可以根据需要多次更改方向,不再需要crash了)。

知道为什么吗?

(顺便说一下,我的项目使用ARC)


仪器/僵尸截图: 在此处输入图像描述


调用的自定义方法heightForRowAtIndexPath

< 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);
}

使用 initWithData 进行 html 转换的自定义方法NSMutableAttributedString

< 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;
}

如果我用一个简单的 initWithString 替换 initWithData 不再有 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;
}
4

1 回答 1

16

我的应用程序中发生了类似的情况。

[NSMutableAttributedString initWithData:]可能需要很长时间才能返回,尤其是对于大量输入。我的猜测是,在执行此调用时,UIKit 旋转处理代码需要运行,但是,由于您的主线程卡在 initWithData: 调用上,所以事情有点不正常。

尝试将解析调用从主线程移开,这样它就不会阻塞它:

+(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);
                 })
            });
    }

也有可能,在您进行轮换时,一些与您的方法相关的对象正在被释放,从而导致 EXC_BAD_ACCESS。您必须对 the- (void)dealloc和 rotation 方法进行一些调试以查看发生了什么。

另一份相关文档如下:

多核注意事项:自 OS X v10.4 起,NSAttributedString 使用 WebKit 进行所有 HTML 文档的导入(但不用于导出)。因为 WebKit 文档加载不是线程安全的,所以在后台线程上使用它并不安全。对于在 OS X v10.5 及更高版本上链接的应用程序,如果 NSAttributedString 在除主线程之外的任何线程上导入 HTML 文档,则 WebKit 的使用将通过 performSelectorOnMainThread:withObject:waitUntilDone: 转移到主线程。这使得操作线程安全,但它要求主线程以一种常见模式执行运行循环。可以通过将标准用户默认 NSRunWebKitOnAppKitThread 的值设置为 YES(无论链接如何获得新行为)或 NO(无论链接如何都获得旧行为)来覆盖此行为。

资源

于 2014-01-22T14:35:50.813 回答