设置UIImageView的图像属性会导致严重滞后

fum*_*007 22 performance asynchronous core-graphics uiimageview uiimage

让我告诉你我遇到的问题以及我是如何解决这个问题的.我有一个UIScrollView加载子视图,从左到右滚动.每个子视图有10-20张图像,每张400x200左右.当我从视图滚动到视图时,我经历了相当多的滞后.

在调查之后,我发现在卸下所有视图并再次尝试后,滞后消失了.我认为图像的同步缓存是滞后的原因.所以我创建了一个UIImageView的子类,它以异步方式加载图像.加载代码如下所示(self.dispatchQueue返回串行调度队列).

- (void)loadImageNamed:(NSString *)name {
    dispatch_async(self.dispatchQueue, ^{
        UIImage *image = [UIImage imageNamed:name];

        dispatch_sync(dispatch_get_main_queue(), ^{
            self.image = image;
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

然而,在将我的所有UIImageViews更改为此子类后,我仍然经历了滞后(我不确定它是否被减少).我把问题的原因归结为self.image = image;.为什么这会导致这么多延迟(但仅限于第一次加载)?

请帮我.=(

fum*_*007 55

编辑2:这是一个包含一些改进的Swift版本.(未经测试.) https://gist.github.com/fumoboy007/d869e66ad0466a9c246d


编辑:实际上,我相信所有必要的是以下内容.(未经测试).

- (void)loadImageNamed:(NSString *)name {
    dispatch_async(self.dispatchQueue, ^{
        // Determine path to image depending on scale of device's screen,
        // fallback to 1x if 2x is not available
        NSString *pathTo1xImage = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];
        NSString *pathTo2xImage = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"png"];

        NSString *pathToImage = ([UIScreen mainScreen].scale == 1 || !pathTo2xImage) ? pathTo1xImage : pathTo2xImage;


        UIImage *image = [[UIImage alloc] initWithContentsOfFile:pathToImage];

        // Decompress image
        if (image) {
            UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);

            [image drawAtPoint:CGPointZero];

            image = UIGraphicsGetImageFromCurrentImageContext();

            UIGraphicsEndImageContext();
        }


        // Configure the UI with pre-decompressed UIImage
        dispatch_async(dispatch_get_main_queue(), ^{
            self.image = image;
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

原始答案:事实证明它不是self.image = image;直接的.UIImage图像加载方法不会立即解压缩和处理图像数据; 他们在视图刷新其显示时执行此操作.因此解决方案是降低Core Graphics的级别并自行解压缩和处理图像数据.新代码如下所示.

- (void)loadImageNamed:(NSString *)name {
    dispatch_async(self.dispatchQueue, ^{
        // Determine path to image depending on scale of device's screen,
        // fallback to 1x if 2x is not available
        NSString *pathTo1xImage = [[NSBundle mainBundle] pathForResource:name ofType:@"png"];
        NSString *pathTo2xImage = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"png"];

        NSString *pathToImage = ([UIScreen mainScreen].scale == 1 || !pathTo2xImage) ? pathTo1xImage : pathTo2xImage;


        UIImage *uiImage = nil;

        if (pathToImage) {
            // Load the image
            CGDataProviderRef imageDataProvider = CGDataProviderCreateWithFilename([pathToImage fileSystemRepresentation]);
            CGImageRef image = CGImageCreateWithPNGDataProvider(imageDataProvider, NULL, NO, kCGRenderingIntentDefault);


            // Create a bitmap context from the image's specifications
            // (Note: We need to specify kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little
            // because PNGs are optimized by Xcode this way.)
            CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
            CGContextRef bitmapContext = CGBitmapContextCreate(NULL, CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetWidth(image) * 4, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);


            // Draw the image into the bitmap context
            CGContextDrawImage(bitmapContext, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);

            //  Extract the decompressed image
            CGImageRef decompressedImage = CGBitmapContextCreateImage(bitmapContext);


            // Create a UIImage
            uiImage = [[UIImage alloc] initWithCGImage:decompressedImage];


            // Release everything
            CGImageRelease(decompressedImage);
            CGContextRelease(bitmapContext);
            CGColorSpaceRelease(colorSpace);
            CGImageRelease(image);
            CGDataProviderRelease(imageDataProvider);
        }


        // Configure the UI with pre-decompressed UIImage
        dispatch_async(dispatch_get_main_queue(), ^{
            self.image = uiImage;
        });
    });
}
Run Code Online (Sandbox Code Playgroud)