代码之家  ›  专栏  ›  技术社区  ›  Kalle

必须在主线程上执行drawInRect:for单独的上下文吗?

  •  1
  • Kalle  · 技术社区  · 14 年前

    drawInRect: 但是在 UIGraphicsBeginImageContext()

    在我的应用程序中,我抓取了一堆大图像,将它们裁剪成缩略图大小,并存储缩略图以供预览。

    关于重绘 UIView

    这段代码相当密集,所以我在一个单独的线程中运行它。实际的扩展看起来是这样的,是在 UIImage

    - (UIImage *) scaledImageWithWidth:(CGFloat)width andHeight:(CGFloat)height
    {
        CGRect rect = CGRectMake(0.0, 0.0, width, height);
        UIGraphicsBeginImageContext(rect.size);
        [self drawInRect:rect]; // <-- crashing on this line
        UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return scaledImage;
    }
    

    这是从一个单独的方法调用的,该方法依次遍历图像并进行处理。对上述方法的实际调用如下所示:

    UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f];
    

    这在大多数情况下都是有效的,但偶尔我会得到一个 EXC_BAD_ACCESS .

    回溯:

    #0  0x330d678c in ripc_RenderImage ()
    #1  0x330dd5aa in ripc_DrawImage ()
    #2  0x300e3276 in CGContextDelegateDrawImage ()
    #3  0x300e321a in CGContextDrawImage ()
    #4  0x315164c8 in -[UIImage drawInRect:blendMode:alpha:] ()
    #5  0x31516098 in -[UIImage drawInRect:] ()
    #6  0x0000d6e4 in -[UIImage(Scaling) scaledImageWithWidth:andHeight:] (self=0x169320, _cmd=0x30e6e, width=48, height=64) at /Users/me/Documents/svn/app/trunk/Classes/UIImage+Scaling.m:20
    #7  0x00027df0 in -[mgMinimap loadThumbnails] (self=0x13df00, _cmd=0x30d05) at /Users/me/Documents/svn/app/trunk/Classes/mgMinimap.m:167
    #8  0x32b15bd0 in -[NSThread main] ()
    #9  0x32b81cfe in __NSThread__main__ ()
    #10 0x30c8f78c in _pthread_start ()
    #11 0x30c85078 in thread_start ()
    

    [更新4]当我在模拟器中运行此操作时,出现此问题,控制台还会显示以下内容:

    // the below is before loading the first thumbnail
    <Error>: CGContextSaveGState: invalid context
    <Error>: CGContextSetBlendMode: invalid context
    <Error>: CGContextSetAlpha: invalid context
    <Error>: CGContextTranslateCTM: invalid context
    <Error>: CGContextScaleCTM: invalid context
    <Error>: CGContextDrawImage: invalid context
    <Error>: CGContextRestoreGState: invalid context
    <Error>: CGBitmapContextCreateImage: invalid context
    // here, the first thumbnail has finished loading and the second one
    // is about to be generated
    <Error>: CGContextSetStrokeColorWithColor: invalid context
    <Error>: CGContextSetFillColorWithColor: invalid context
    

    图纸内容: 而操作系统也在尝试绘制一些东西,偶尔会导致崩溃。我一直认为只要你不在实际屏幕上画画,这是可以接受的——不是这样吗?或者如果是这样的话,你知道是什么原因造成的吗?

    Update(r2):我忘了提到这个应用程序是在非常严重的内存限制下运行的(我在任何给定的时间都加载了很多图像,这些图像被交换进/出),所以这可能是一个内存不足的情况(请继续阅读——事实并非如此)。不过,我不知道如何验证这一点,所以也欢迎对此发表看法。我确实通过严格减少正在加载的图像的数量并添加一个检查来验证这一点,以确保它们被正确释放(它们被释放了,崩溃仍然发生)。

    更新3:我以为我发现了问题。下面是我在坠机事件再次发生前写下的答案:

    代码(在我发布这个问题之后)偶尔会以退出代码开始退出 0 10 (SIGBUS) . 0 10 似乎意味着一切,所以这是无济于事。这个 图纸内容: 不过,当车祸发生时,这个电话是一个很大的暗示。

    for

    - (void)loadThumbnails
    {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        for (...) {
            NSAutoreleasePool *cyclePool = 
               [[NSAutoreleasePool alloc] init]; // <-- here
            UIImage *bigger = ...;
            UIImage *small = [bigger scaledImageWithWidth:24.f andHeight:32.f];
            UIImage *bloated = [i scaledImageWithWidth:48.f andHeight:64.f];
            [cyclePool release]; // <-- ending here
        }
        [pool release];
    }
    

    3 回复  |  直到 14 年前
        1
  •  2
  •   Matt Long    14 年前

    你看过马特·杰梅尔的最新版本吗, MGImageUtilities ? 我从他在github上的资料中提取了这个:

    // Create appropriately modified image.
    UIImage *image;
    UIGraphicsBeginImageContextWithOptions(destRect.size, NO, 0.0); // 0.0 for scale means "correct scale for device's main screen".
    CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect); // cropping happens here.
    image = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage.
    [image drawInRect:destRect]; // the actual scaling happens here, and orientation is taken care of automatically.
    CGImageRelease(sourceImg);
    image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    

    不确定线程安全是否是问题所在,但在走得太远之前,不妨尝试一下Matt的代码。

        2
  •  1
  •   Kalle    14 年前

    原来,有两个答案:

    “必须 drawInRect: 在主线程上执行一个单独的上下文?“答案是不,它没有。

    然而, UIGraphicsBeginImageContext 必须。事实上,这就是发生撞车事故的原因。直到(无效的)图形上下文被更改,崩溃才显露出来,这就是为什么崩溃发生在 图纸内容: 线路。

    UIGraphicsContext CGBitmapContext 线程安全。

        3
  •  0
  •   davbryn    14 年前

    我觉得你不该打电话来 drawInRect setNeedsDisplay 在将向发送消息的视图上 提取率 在安全的情况下这样做。