4

为了复制我在应用程序中遇到的崩溃,我必须创建一个重复率略高的示例,这可能不切实际,但可以准确地演示我的应用程序中发生的情况。在使用 NSString 在后台线程上绘制 NSString 时NSOperations,有时会发生崩溃,崩溃之前堆栈跟踪上的最后一次调用是WebCore::FontFallbackList::~FontFallBackList().

- (void)viewDidLoad
{
    queue = [[NSOperationQueue alloc] init];
    [NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
}

-(void)timerDidFire:(NSTimer*)timer
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        CGRect rect = CGRectMake(0, 0, 50, 50);
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(rect.size.width, rect.size.height), YES, 0.0);
        NSString *string = @"Sd";
        [string drawInRect:rect withFont:[UIFont boldSystemFontOfSize:12] lineBreakMode:UILineBreakModeTailTruncation]; 
        UIGraphicsEndImageContext();     
    }];
    [queue addOperation:op];
}

您可以使用上面的代码轻松复制此崩溃。任何人都对这次崩溃的性质以及为什么会发生有任何见解?(这个问题的解决方法是设置[queue setMaxConcurrentOperations:1];

4

3 回答 3

5

这似乎是 iOS 5.x 中的回归:它发生在 5.0 和 5.1 模拟器以及 5.1 设备上,但不会在 4.3 模拟器或 4.3.2 设备上发生。

特别是似乎被破坏的字符串绘图 - 如果您所做的只是字符串绘图(避免创建/销毁上下文的开销),崩溃几乎立即发生:

-(void)threadFunc:(UIFont *)font {
  @autoreleasepool {
    NSString *string = @" ";
    CGRect r = {{0,0},{50,50}};
    UIGraphicsBeginImageContextWithOptions(r.size, YES, 0);
    for(;;) {
      @autoreleasepool {
        [string drawAtPoint:r.origin withFont:font];
      }
    }
    UIGraphicsEndImageContext();
  }
}

-(void)startThreads
{
  UIFont * font = [UIFont systemFontOfSize:12];
  for (int i = 2; i--;)
  {
    [NSThread detachNewThreadSelector:@selector(threadFunc:) toTarget:self withObject:font];
  }
}

编辑:它仅在多核环境中“几乎即时”(即双核设备或模拟器,假设是多核 Mac)。否则,崩溃大约需要 10-20 分钟。我没有双核 iOS 4.x 设备(唯一的可能性似乎是 iPad 2),但一个多小时后单核设备没有崩溃。

我已经向 Apple 提出了一个错误,如果它影响到你,我鼓励你也这样做。

于 2012-07-25T18:23:13.370 回答
1

一个简单的解决方法是用 NSAttributedString 替换 NSString。

如果像这样修改,@maq 列出的崩溃测试不会崩溃:

-(void)threadFunc:(UIFont *)font {
    @autoreleasepool {
        NSString *string = @" ";
        NSMutableAttributedString *test = [[NSMutableAttributedString alloc] initWithString:string];
        CGRect r = {{0,0},{50,50}};
        UIGraphicsBeginImageContextWithOptions(r.size, YES, 0);
        for(;;) {
            @autoreleasepool {
                 UIFont *font=[UIFont fontWithName:@"Helvetica-Bold" size:30.0f];
                [test addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, test.length)];
                [test drawAtPoint:r.origin];
            }
        }
        UIGraphicsEndImageContext();
    }
}
于 2013-07-24T22:00:47.713 回答
-1

我不相信 UIKit 是线程安全的,从后台线程绘制非常时髦。它可能有效,也可能无效。

UIGraphicsBeginContext 的文档实际上明确指出您应该只从主线程调用它。显然,多线程完全不在讨论范围内。

于 2012-07-21T05:41:41.423 回答