【问题标题】:NSMutableAttributedString initWithData: causing EXC_BAD_ACCESS on rotationNSMutableAttributedString initWithData:导致 EXC_BAD_ACCESS 旋转
【发布时间】:2014-01-10 21:24:30
【问题描述】:

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

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

除了我旋转屏幕时,一切都完美无缺 => 我每次都有 exc_bad_access。

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

(当我删除此方法并使用initWithString 创建一个“简单”NSMutableAttributedString 时,我可以随意更改方向,不再需要crash

知道为什么吗?

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


仪器/僵尸截图:


heightForRowAtIndexPath 中调用的自定义方法:

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的转换:

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

【问题讨论】:

    标签: ios ios7 exc-bad-access nsmutableattributedstring


    【解决方案1】:

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

    [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。您必须对 - (void)dealloc 和旋转方法进行一些调试,以查看发生了什么。

    另一篇相关文档如下:

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

    Source

    【讨论】:

    • 谢谢,你可能是对的(所以我接受了答案)。我采用 TTTAttributedLabel 方式,因为我还希望在这些属性字符串中拥有可点击的“链接”。它运行良好(经过一段时间学习如何正确使用它)= 非常快 + 灵活且易于集成/管理 + 委托方法
    • 我要补充一点,Apple 明确表示不要在后台线程上使用 UIKit 版本。我通过使用 performSelectorOnMainThread:object:waitUntilDone: 解决了这个问题,它就像一个魅力。
    • @Jesse Naugher 我意识到这有点老了,但是您如何将performSelectorOnMainThread:object:waitUntilDone: 与您评论的这个答案结合使用?遇到类似问题并寻求解决方案。
    • @arie 在答案中提到的使用 dispatch_async 的修复可能是处理这个问题的首选语法(使用 GCD 的更现代的语法)。但我相信我只有一个辅助方法,我刚刚用-performSelectorOnMainThread 调用它,它执行了实际的属性字符串处理。
    • 关于这个答案——我认为最好在后台线程上创建一个单独的串行队列来处理所有这些,而不是每次都创建一个新的后台队列。否则,您将面临一次创建多个线程的风险,因为此方法在-tableView:cellForRowAtIndexPath 中被调用。在 OP 的情况下,他们也在 -tableView:heightForRowAtIndexPath: 中调用了这个方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-27
    • 2016-11-19
    • 2012-10-02
    • 2011-04-14
    • 2017-03-15
    • 2014-09-27
    • 1970-01-01
    相关资源
    最近更新 更多