在UI线程上懒洋洋地加载CGImage / UIImage会导致口吃 [英] CGImage/UIImage lazily loading on UI thread causes stutter

查看:149
本文介绍了在UI线程上懒洋洋地加载CGImage / UIImage会导致口吃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的程序显示一个水平滚动表面,从左到右平铺UIImageViews。代码在UI线程上运行,以确保新显示的UIImageViews分配了一个新加载的UIImage。加载发生在后台线程上。



一切都很好,除了每个图像变得清晰可见时都会出现断断续续的情况。起初我以为我的后台工作者在UI线程中锁定了一些内容。我花了很多时间查看它,并最终意识到UIImage在UI线程首次可见时会对UI线程进行一些额外的延迟处理。这让我很困惑,因为我的工作线程有明确的解压缩JPEG数据的代码。



无论如何,在预感中,我写了一些代码来渲染到后台线程上的临时图形上下文 - 果然,口吃消失了。 UIImage现在正在我的工作线程上预先加载。到目前为止一直很好。



问题在于我的新强力懒惰图像方法不可靠。它会导致间歇性的EXC_BAD_ACCESS。我不知道UIImage在幕后实际做了什么。也许它正在解压缩JPEG数据。无论如何,方法是:

  +(void)forceLazyLoadOfImage:(UIImage *)image 
{
CGImageRef imgRef = image.CGImage;

CGFloat currentWidth = CGImageGetWidth(imgRef);
CGFloat currentHeight = CGImageGetHeight(imgRef);

CGRect bounds = CGRectMake(0.0f,0.0f,1.0f,1.0f);

CGAffineTransform transform = CGAffineTransformIdentity;
CGFloat scaleRatioX = bounds.size.width / currentWidth;
CGFloat scaleRatioY = bounds.size.height / currentHeight;

UIGraphicsBeginImageContext(bounds.size);

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context,scaleRatioX,-scaleRatioY);
CGContextTranslateCTM(context,0,-currentHeight);
CGContextConcatCTM(context,transform);
CGContextDrawImage(context,CGRectMake(0,0,currentWidth,currentHeight),imgRef);

UIGraphicsEndImageContext();
}

EXC_BAD_ACCESS发生在CGContextDrawImage行。问题1:我是否允许在UI线程以外的线程上执行此操作?问题2:什么是UIImage实际上是预加载?问题3:解决这个问题的官方方法是什么?



感谢您阅读所有内容,我们将非常感谢您的建议!

解决方案

UIGraphics * 方法仅用于从主线程调用。它们可能是你麻烦的根源。



你可以通过调用<$ c来替换 UIGraphicsBeginImageContext() $ C> CGBitmapContextCreate();它需要更多一些(你需要创建一个颜色空间,找出合适大小的缓冲区来创建,并自己分配)。 CG * 方法可以从不同的线程运行。






<我不确定你是如何初始化UIImage的,但如果你是用 imageNamed: initWithFile:然后您可以通过自己加载数据然后调用 initWithData:来强制加载它。口吃可能是由于懒惰的文件I / O,因此使用数据对象初始化它不会给它提供从文件读取的选项。


My program displays a horizontal scrolling surface tiled with UIImageViews from left to right. Code runs on the UI thread to ensure that newly-visible UIImageViews have a freshly loaded UIImage assigned to them. The loading happens on a background thread.

Everything works almost fine, except there is a stutter as each image becomes visible. At first I thought my background worker was locking something in the UI thread. I spent a lot of time looking at it and eventually realized that the UIImage is doing some extra lazy processing on the UI thread when it first becomes visible. This puzzles me, since my worker thread has explicit code for decompressing JPEG data.

Anyway, on a hunch I wrote some code to render into a temporary graphics context on the background thread and - sure enough, the stutter went away. The UIImage is now being pre-loaded on my worker thread. So far so good.

The issue is that my new "force lazy load of image" method is unreliable. It causes intermittent EXC_BAD_ACCESS. I have no idea what UIImage is actually doing behind the scenes. Perhaps it is decompressing the JPEG data. Anyway, the method is:

+ (void)forceLazyLoadOfImage: (UIImage*)image
{
 CGImageRef imgRef = image.CGImage;

 CGFloat currentWidth = CGImageGetWidth(imgRef);
 CGFloat currentHeight = CGImageGetHeight(imgRef);

    CGRect bounds = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);

 CGAffineTransform transform = CGAffineTransformIdentity;
 CGFloat scaleRatioX = bounds.size.width / currentWidth;
 CGFloat scaleRatioY = bounds.size.height / currentHeight;

 UIGraphicsBeginImageContext(bounds.size);

 CGContextRef context = UIGraphicsGetCurrentContext();
 CGContextScaleCTM(context, scaleRatioX, -scaleRatioY);
 CGContextTranslateCTM(context, 0, -currentHeight);
 CGContextConcatCTM(context, transform);
 CGContextDrawImage(context, CGRectMake(0, 0, currentWidth, currentHeight), imgRef);

 UIGraphicsEndImageContext();
}

And the EXC_BAD_ACCESS happens on the CGContextDrawImage line. QUESTION 1: Am I allowed to do this on a thread other than the UI thread? QUESTION 2: What is the UIImage actually "pre-loading"? QUESTION 3: What is the official way to solve this problem?

Thanks for reading all that, any advice would be greatly appreciated!

解决方案

The UIGraphics* methods are designed to be called from the main thread only. They are probably the source of your trouble.

You can replace UIGraphicsBeginImageContext() with a call to CGBitmapContextCreate(); it's a little more involved (you need to create a color space, figure out the right sized buffer to create, and allocate it yourself). The CG* methods are fine to run from a different thread.


I'm not sure how you're initializing UIImage, but if you're doing it with imageNamed: or initWithFile: then you might be able to force it to load by loading the data yourself and then calling initWithData:. The stutter is probably due to lazy file I/O, so initializing it with a data object won't give it the option of reading from a file.

这篇关于在UI线程上懒洋洋地加载CGImage / UIImage会导致口吃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆