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
这个第三方库封装得很好,使用起来与我们以前用他来加载网络图片方式一样,如下:
[p_w_picpathView sd_setImageWithURL:[NSURL URLWithString:图片路径] placeholderImage:[UIImage p_w_picpathNamed:@"默认图片"] completed:^(UIImage *p_w_picpath, NSError *error, SDImageCacheType cacheType, NSURL *p_w_picpathURL) { }];
不过,我们要深入看看他究竟是怎么实现的。
我们打开:
SDWebImageDownloaderOperation
这个类继承了NSOperation,主要使用NSUrlSession来下载网络图片,我们来看他下载完成的委托方法:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
我们截取部分代码块来集中分析一下:
UIImage *p_w_picpath = [UIImage sd_p_w_picpathWithData:self.p_w_picpathData];调试进去:UIImage *p_w_picpath;NSString *p_w_picpathContentType = [NSData sd_contentTypeForImageData:data]; //根据数据流的前8位来判断图片类型if ([p_w_picpathContentType isEqualToString:@"p_w_picpath/gif"]) {p_w_picpath = [UIImage sd_animatedGIFWithData:data];}#ifdef SD_WEBP else if ([p_w_picpathContentType isEqualToString:@"p_w_picpath/webp"]){p_w_picpath = [UIImage sd_p_w_picpathWithWebPData:data]; //将WebP解码成相应的格式(可能是jpg,png等)}#endifelse {p_w_picpath = [[UIImage alloc] initWithData:data];UIImageOrientation orientation = [self sd_p_w_picpathOrientationFromImageData:data]; if (orientation != UIImageOrientationUp) {p_w_picpath = [UIImage p_w_picpathWithCGImage:p_w_picpath.CGImagescale:p_w_picpath.scaleorientation:orientation];}}
我们来说下sd_contentTypeForImageData 这个方法,如下:
+ (NSString *)sd_contentTypeForImageData:(NSData *)data {uint8_t c;[data getBytes:&c length:1]; switch (c) { case 0xFF: return @"p_w_picpath/jpeg"; case 0x89: return @"p_w_picpath/png"; case 0x47: return @"p_w_picpath/gif"; case 0x49: case 0x4D: return @"p_w_picpath/tiff"; case 0x52: // R as RIFF for WEBPif ([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 @"p_w_picpath/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_p_w_picpathWithWebPData 这个方法
里面封装了将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 && p_w_picpath && !downloadedImage) { // Image refresh hit the NSURLCache cache, do not call the completion block} else if (downloadedImage && (!downloadedImage.p_w_picpaths || (options & SDWebImageTransformAnimatedImage))) {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{UIImage *transformedImage = [self transformDownloadedImage:downloadedImage p_w_picpathData:data withURL:url]; //存储以前,是否要将nsdata转换为其他格式的图片对象if (transformedImage && finished) {BOOL p_w_picpathWasTransformed = ![transformedImage isEqual:downloadedImage];[self.p_w_picpathCache storeImage:transformedImage recalculateFromImage:p_w_picpathWasTransformed p_w_picpathData:(p_w_picpathWasTransformed ? 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.p_w_picpathCache storeImage:downloadedImage recalculateFromImage:NO p_w_picpathData: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 p_w_picpathData:data withURL:url];
也算是用心良苦,因为可能考虑到WebP的解码会耗费一些时间(测试下来发现,120k左右的WebP,解码会耗时30ms左右),所以提供一个委托,可以选择将WebP的NSData转换为png或者jpg之后,再存储到内存,再存储到磁盘。
不过,时间与空间就像鱼和熊掌,不可兼得,如果选择节省时间,就不可避免的要占用更大的空间。到底选时间还是选空间,仁者见仁智者见智吧。
WebP的劣势
把WebP说得这么天花乱坠,但是WebP也是有自己的劣势的:
压缩时间长,大概是png的8倍左右(不过一般都是在服务端压缩,客户端解码,所以服务端可以做个预压缩)
解码时间比png长,大概几十毫秒。WebP是节省了流量(图片小),增加了解码时间,换句话说就是:同样的图片,网络越快(图片更小的WebP就没有明显优势),图片越多(WebP要解码),WebP比png要慢。
UIWebView,WKWebView都不支持WebP。(UIWebView可以用NSUrlProtocol来解决,但是WKWebView还没有太完美的办法,谁知道的请告诉我下)
不支持流式解压缩(即图片加载的时候会由模糊慢慢变清晰的过程,WebP貌似不支持这种解压缩方式)
最后
关于WebP和jpg的图片大小来比较的话,因为WebP是支持无损和有损压缩的,而jpg是有损压缩的格式,所以如果同样的图片都做有损压缩,WebP是比jpg要小的。
转载于:https://blog.51cto.com/huyusheng/1924137
iOS性能之WebP相关推荐
- iOS 客户端基于 WebP 图片格式的流量优化(下)
在iOS 客户端基于 WebP 图片格式的流量优化(上)这篇文章中,已经介绍了WebP格式图片的下载使用,仅仅只有这样还远远不够,还需要对已经下载的图片数据进行缓存. 曾经有句名言『计算机世界有两大难 ...
- iOS 性能优化总结
原文链接:https://github.com/skyming/iOS-Performance-Optimization 关于 iOS 性能优化梳理: 基本工具.业务优化.内存优化.卡顿优化.布局优化 ...
- IOS性能调优系列:使用Time Profiler发现性能瓶颈
硬广:<IOS性能调优系列>第五篇,预计会有二十多篇,持续更新,欢迎关注. 之前四篇都是关注于内存方面,分析了内存泄漏.僵尸对象.内存分配,本篇介绍Time Profiler工具的使用,开 ...
- iOS性能优化之耗电量
iOS性能优化之耗电量 前言 最近在测试App的时候,发现手机特别容易发烫,我们都知道 ,如果手机容易发烫,那么耗电量肯定会相当大,手机电量使用的时间也会相对少:对此,我在工作之余抽了点时间,对手机的 ...
- 处理器最新排行_最新 iOS 性能排行榜,你的设备落伍了吗?
iOS性能排行 近日国内知名跑分软件安兔兔,发布了三月份的 iOS 设备性能排行榜,虽然安兔兔的跑分一直被人质疑不准确,很娱乐. 但毕竟是国内最大且唯一的老牌跑分软件,其得出的综合跑分还是值得参考的. ...
- 苹果处理器排行_最新 iOS 性能排行榜,你的设备落伍了吗?
iOS性能排行 近日国内知名跑分软件安兔兔,发布了三月份的 iOS 设备性能排行榜,虽然安兔兔的跑分一直被人质疑不准确,很娱乐. 但毕竟是国内最大且唯一的老牌跑分软件,其得出的综合跑分还是值得参考的. ...
- iOS 性能监控(一)—— CPU功耗监控
前言 最近,在看戴铭老师关于 "性能监控" 相关的技术分享,感觉收获很多.基于最近的学习,总结了一些性能监控相关的实践,并计划落地一系列 "性能监控" 相关的文 ...
- 使用纯 python 实现 Instruments 协议,跨平台 (win,mac,linux) 获取 iOS 性能数据
原文由YueChen发表于TesterHome社区网站,点击原文链接可与YueChen交流. 前言 获取 iOS 性能数据,一直都是比较麻烦的事情,之前在构建测试框架&平台的时候,获取 iOS ...
- IOS性能调优系列:使用Zombies动态分析内存中的僵尸对象
硬广:<IOS性能调优系列>第四篇,预计会有二十多篇,持续更新,欢迎关注. 前两篇<IOS性能调优系列:Analyze静态分析>.<IOS性能调优系列:使用Instrum ...
最新文章
- 读书笔记之《程序员必读的职业规划书》
- mysql math.max_Math.max.apply()用法
- 从远程服务器拷贝文件命令,远程拷贝文件命令Scp的使用
- iOS开发,轻松获取根控制器当前控制器的正确方式
- fastclick.js插件使用简单说明
- NetBeans Support Weblog
- DoIP(三)—— 通信流程
- Spring Cloud 快速入门指南(二)
- 计算机网络笔记2 应用层
- const T 与T const(const T vs.T const的翻译 Dan Saks)
- 华为手机信息不弹屏了为什么_华为手机验证码不弹出是怎么回事
- 开机弹框显示IGCCTray.exe异常的修复方式
- IDEA 一直卡在Buil(编译 write classes)报错资源不足
- php 网址尾部带斜杠和不带区别,网址中带斜杠和不带斜杠的区别
- 如何解决App inventor和AI伴侣无法连接的问题
- 还在为不会做PPT而担忧嘛,有这个PPT神器,从此秒变王者!
- 用R检验配对股票的协整性
- Catch fox game 抓狐狸Python代码实现
- SpringBoot实现多数据源(二)【Mybatis插件】
- linux设备巡检指令,Linux系统巡检常用命令