iOS性能之WebP

简介:

当今互联网,无论网页还是APP,流量占用最大的,多数都是因为图片,越是良好的用户体验,对图片的依赖度越高。但是图片是一把双刃剑,带来了用户体验,吸引了用户注意,却影响了性能,因为网络请求时间会相对比较长。

图片分很多种,比较主流的就是:位图(BMP),jpg(JPEG,有损压缩格式),png(无损压缩格式)等,这三种,按照图片大小和清晰度来看,依次是:BMP > png > jpg。因为jpg是有损压缩格式,所以jpg图片相对最小。iOS普遍选择的是png来作为最优先选择的图片(苹果官方也是这样建议的)。

不过,有一种图片格式,在大小上比png小,图片质量上跟png差不多,就是WebP。

什么是WebP?

简单描述一下,WebP是google创造出的一种图片格式,图片的压缩和解码都由google提供的API完成(各种语言都有,不过目前好像没看到js可以解码WebP的),在无损压缩的情况下,比png要小28%左右

现在已经被各大浏览器厂商兼容(如:Chrome,Firefox等),不过苹果的Safri还没有兼容这种格式,所以如果UIWebView里面含有WebP的图片的话,就会显示不出来(但是我们可以通过NSUrlProtocol来做处理)。如果要在APP中使用得话,我们需要引入SDWebImage这个第三方库。

SDWebImage使用WebP

这个第三方库封装得很好,使用起来与我们以前用他来加载网络图片方式一样,如下:

[imageView sd_setImageWithURL:[NSURL URLWithString:图片路径] placeholderImage:[UIImage imageNamed:@"默认图片"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { }];

不过,我们要深入看看他究竟是怎么实现的。

我们打开:

SDWebImageDownloaderOperation

这个类继承了NSOperation,主要使用NSUrlSession来下载网络图片,我们来看他下载完成的委托方法:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error

我们截取部分代码块来集中分析一下:

UIImage *image = [UIImage sd_imageWithData:self.imageData];

调试进去:

    UIImage *image;
    NSString *imageContentType = [NSData sd_contentTypeForImageData:data]; //根据数据流的前8位来判断图片类型
    if ([imageContentType isEqualToString:@"image/gif"]) {
        image = [UIImage sd_animatedGIFWithData:data];
    }#ifdef SD_WEBP    else if ([imageContentType isEqualToString:@"image/webp"])
    {
        image = [UIImage sd_imageWithWebPData:data]; //将WebP解码成相应的格式(可能是jpg,png等)    }#endif
    else {
        image = [[UIImage alloc] initWithData:data];
        UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];        if (orientation != UIImageOrientationUp) {
            image = [UIImage imageWithCGImage:image.CGImage
                                        scale:image.scale
                                  orientation:orientation];
        }
    }

  • 我们来说下sd_contentTypeForImageData 这个方法,如下:

+ (NSString *)sd_contentTypeForImageData:(NSData *)data {
    uint8_t c;
    [data getBytes:&c length:1];    switch (c) {        case 0xFF:            return @"image/jpeg";        case 0x89:            return @"image/png";        case 0x47:            return @"image/gif";        case 0x49:        case 0x4D:            return @"image/tiff";        case 0x52:            // R as RIFF for WEBP
            if ([data length] < 12) {                return nil;
            }

            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {                return @"image/webp";
            }            return nil;
    }    return nil;
}

里面的uint8_t就是取NSData的前8位,因为图片变换成NSData后,是使用得ASCII码来表示的,每种图片都含有固定的头信息块。

png是:89 50 4E 47 0D 0A 1A 0A

bmp是:42 4D

jpg是:FF D8 FF

webp是:52 49 46 46 中间4个字符不定 57 45 42 50(翻译过来就是:RIFF 其他4个字符 WEBP)

这样来看,上面代码的含义就比较清楚了。

如果想深入了解一下图片格式及组成,这里有一篇不错的文章:

http://blog.csdn.net/hherima/article/details/45846901

  • 我们再来看看 sd_imageWithWebPData 这个方法

里面封装了将WebP解码成其他格式图片的过程。WebP是采用VP8的编码格式。有兴趣可以研究一下具体的算法实现过程,这里有几篇文章介绍WebP的压缩算法。

https://developers.google.com/speed/webp/docs/compression

http://blog.csdn.net/leixiaohua1020/article/details/12760173

  • 提醒

SDWebImage在对WebP做存储的时候,存的是未解码的NSData,而不是解码后的NSData,如下代码:

SDWebImageManager 里面的if (options & SDWebImageRefreshCached && image && !downloadedImage) {                        // Image refresh hit the NSURLCache cache, do not call the completion block                    }                    else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage))) {
                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                            UIImage *transformedImage = [self transformDownloadedImage:downloadedImage imageData:data withURL:url];   //存储以前,是否要将nsdata转换为其他格式的图片对象
                            if (transformedImage && finished) {
                                BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
                            }
                            
                            dispatch_main_sync_safe(^{                                if (strongOperation && !strongOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }                    else {                        if (downloadedImage && finished) {
                            [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];    //WebP的存储走的是这一步                        }
                        
                        dispatch_main_sync_safe(^{                            if (strongOperation && !strongOperation.isCancelled) {
                                completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
                            }
                        });
                    }

里面提供了一个委托:

UIImage *transformedImage = [self transformDownloadedImage:downloadedImage imageData:data withURL:url];

也算是用心良苦,因为可能考虑到WebP的解码会耗费一些时间(测试下来发现,120k左右的WebP,解码会耗时30ms左右),所以提供一个委托,可以选择将WebP的NSData转换为png或者jpg之后,再存储到内存,再存储到磁盘。

不过,时间与空间就像鱼和熊掌,不可兼得,如果选择节省时间,就不可避免的要占用更大的空间。到底选时间还是选空间,仁者见仁智者见智吧。

WebP的劣势

把WebP说得这么天花乱坠,但是WebP也是有自己的劣势的:

  1. 压缩时间长,大概是png的8倍左右(不过一般都是在服务端压缩,客户端解码,所以服务端可以做个预压缩)

  2. 解码时间比png长,大概几十毫秒。WebP是节省了流量(图片小),增加了解码时间,换句话说就是:同样的图片,网络越快(图片更小的WebP就没有明显优势),图片越多(WebP要解码),WebP比png要慢。

  3. UIWebView,WKWebView都不支持WebP。(UIWebView可以用NSUrlProtocol来解决,但是WKWebView还没有太完美的办法,谁知道的请告诉我下)

  4. 不支持流式解压缩(即图片加载的时候会由模糊慢慢变清晰的过程,WebP貌似不支持这种解压缩方式)


















本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1929785 ,如需转载请自行联系原作者



相关文章
|
3月前
|
监控 测试技术 iOS开发
查看ios 应用程序性能
查看ios 应用程序性能
37 0
|
2月前
|
监控 API iOS开发
克魔助手 - iOS性能检测平台
众所周知,如今的用户变得越来越关心app的体验,开发者必须关注应用性能所带来的用户流失问题。目前危害较大的性能问题主要有:闪退、卡顿、发热、耗电快、网络劫持等,但是做过iOS开发的人都知道,在开发过程中我们没有一个很直观的工具可以实时的知道开发者写出来的代码会不会造成性能问题,虽然Xcode里提供了耗电量检测、内存泄漏检测等工具,但是这些工具使用效果并不理想(如Leak无法发现循环引用造成的内存泄漏)。所以这篇文章主要是介绍一款实时监控app各项性能指标的工具,包括CPU占用率、内存使用量、内存泄漏、FPS、卡顿检测,并且会分析造成这些性能问题的原因。
|
3月前
|
监控 Linux iOS开发
如何使用克魔开发助手优化iOS应用性能
如何使用克魔开发助手优化iOS应用性能
32 1
|
API iOS开发
iOS 关于图片缩放性能探究
iOS 关于图片缩放性能探究
iOS 关于图片缩放性能探究
|
iOS开发
《移动 App 性能监测实践(iOS篇)》电子版地址
移动 App 性能监测实践(iOS篇)
129 0
《移动 App 性能监测实践(iOS篇)》电子版地址
|
存储 iOS开发 UED
iOS 性能检测新方式​——AnimationHitches
iOS 性能检测新方式​——AnimationHitches
iOS 性能检测新方式​——AnimationHitches
|
机器学习/深度学习 安全 测试技术
阿里云EMAS-专家测试服务iOS和Android上百种机型性能、兼容及UI等测试
阿里云EMAS测试专家有着集团内部多个日活过亿规模APP经验,提供EMAS专家测试,客户只需提交测试需求,从用例设计、脚本录制、海量机型测试、整理测试结果、48小时输出专家测试报告均由阿里云EMAS测试专家一站式服务完成。覆盖功能测试、深度兼容测试、性能测试、UI适配测试以及隐私合规检测等,帮助用户以更低成本获得高质量的全面测试能力,可用于APP正式发版前验收,规避手机APP上线前或发版过程中各类隐患。
407 0
阿里云EMAS-专家测试服务iOS和Android上百种机型性能、兼容及UI等测试
|
存储 缓存 API
Metal新特性:大幅度提升iOS端性能
作为较早在客户端侧选择Flutter方案的技术团队,性能和用户体验一直是闲鱼技术团队在开发中比较关注的点。而Metal这样的直接操作GPU的底层接口无疑会给闲鱼技术团队突破性能瓶颈提供一些新的思路。 本文将会详细阐述一下这次大会Metal相关的新特性,以及对于闲鱼技术和整个淘系技术来说,这些新特性带来了哪些技术启发与思考。
Metal新特性:大幅度提升iOS端性能
|
存储 JSON 监控
iOS 测试 | iOS 自动化性能采集
前言 对于iOS总体生态是比较封闭的,相比Android没有像adb这种可以查看内存、cpu的命令.在日常做性能测试,需要借助xcode中instruments查看内存、cpu等数据.
iOS 测试 | iOS 自动化性能采集
基于iOS平台的性能检测方案
导语 在开发过程中,功能不仅要满足业务需求,也要关注功能对App性能带来的一些问题。开发人员在开发阶段检测性能比较容易,iOS端可以直接通过instruments工具进行检测。但是在测试阶段,测试人员要检测性能需要下载开发工具成本比较高。
1604 0