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:];
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] < ) {
return nil;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(, )] 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, ), ^{
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也是有自己的劣势的:
- 压缩时间长,大概是png的8倍左右(不过一般都是在服务端压缩,客户端解码,所以服务端可以做个预压缩)
- 解码时间比png长,大概几十毫秒。WebP是节省了流量(图片小),增加了解码时间,换句话说就是:同样的图片,网络越快(图片更小的WebP就没有明显优势),图片越多(WebP要解码),WebP比png要慢。
- UIWebView,WKWebView都不支持WebP。(UIWebView可以用NSUrlProtocol来解决,但是WKWebView还没有太完美的办法,谁知道的请告诉我下)
- 不支持流式解压缩(即图片加载的时候会由模糊慢慢变清晰的过程,WebP貌似不支持这种解压缩方式)
最后
关于WebP和jpg的图片大小来比较的话,因为WebP是支持无损和有损压缩的,而jpg是有损压缩的格式,所以如果同样的图片都做有损压缩,WebP是比jpg要小的。
这里有篇不错的介绍WebP的文章:
https://isux.tencent.com/introduction-of-webp.html
iOS性能之WebP的更多相关文章
- 【腾讯Bugly干货分享】微信读书iOS性能优化
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 “8小时内拼工作,8小时外拼成长 ...
- iOS性能优化:Instruments使用实战
iOS性能优化:Instruments使用实战 最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧,小结如下. Instrument ...
- IOS性能调优系列:Analyze静态分析
目前关于IOS性能优化的教程较少,决定写一个<IOS性能调优系列>,主要关注与内存泄漏.性能优化.流量和电量分析几个方面. XCode已经提供了非常强大的性能调优工具,结合几个第三方工具和 ...
- IOS性能调优系列:使用Instruments动态分析内存泄漏
硬广:<IOS性能调优系列>第二篇,持续更新,欢迎关注. 第一篇介绍了Analyze对App做静态分析,可以发现应用中的内存泄漏问题,对于有些内存泄漏情况通过静态分析无法解决的,可以通过动 ...
- IOS 性能优化的建议和技巧
IOS 性能优化的建议和技巧 本文来自iOS Tutorial Team 的 Marcelo Fabri,他是Movile的一名 iOS 程序员.这是他的个人网站:http://www.marcelo ...
- iOS性能之其他
本篇文章是个引用,因为这些技术我都只是研究过,但是并没有在项目中使用,也没有深入研究,所以只能当做一个笔记了 网络请求 现在大多数的网络请求都是使用的json格式(相信没有APP再使用XML格式了吧) ...
- iOS性能优化总结
iOS性能优化总结.关于 iOS 性能优化梳理: 基本工具.业务优化.内存优化.卡顿优化.布局优化.电量优化. 安装包瘦身.启动优化.网络优化等. 关于iOS 性能优化梳理: 基本工具.业务优化.内存 ...
- IOS性能调优系列:使用Time Profiler发现性能瓶颈
硬广:<IOS性能调优系列>第五篇,预计会有二十多篇,持续更新,欢迎关注. 之前四篇都是关注于内存方面,分析了内存泄漏.僵尸对象.内存分配,本篇介绍Time Profiler工具的使用,开 ...
- IOS性能调优系列:使用Zombies动态分析内存中的僵尸对象
硬广:<IOS性能调优系列>第四篇,预计会有二十多篇,持续更新,欢迎关注. 前两篇<IOS性能调优系列:Analyze静态分析>.<IOS性能调优系列:使用Instrum ...
随机推荐
- 点击弹窗后再刷新html页面
当alert弹出框点击确定以后,再让页面重新加载一下 具体的代码如下: <script type="text/javascript"> alert("签到成功 ...
- arcpy.mapping常用四大件-MapsurroundElement
arcpy.mapping常用四大件-MapsurroundElement by 李远祥 在arcpy.mapping 中,除了数据入口MapDocument.图层Layer之外,另一重要的角色就是M ...
- ArcGIS制图表达Representation-规则和几何效果
ArcGIS制图表达Representation-规则和几何效果 by 李远祥 对制图表达运用是否纯熟,主要是看对制图表达规则的理解和巧妙运用.前面章节已经粗略介绍过制图表达的运用,通过一些简单的规则 ...
- windows visual studio 2015安装
下载 首先到微软官方下载在线安装文件 https://www.microsoft.com 运行安装 点击运行后选择 自定义 选择安装功能,把sql server去掉,是数据库插件 点击下一步,中途会提 ...
- WKWebView的使用与JS交互详细解读
前言: WKWebView 这是在iOS8.0之后增加的一个比UIWebView更加完善和强大的控件!看网上关于它的博客也是有许多的了,从各个方面总结一下这个WKWebView看网上说它主要是为了和J ...
- wamp修改空密码以及设置虚拟站点
近来重装了一下wamp,索性记录一下,wamp安装完后,我的常用配置.首先,肯定要修改默认的空密码:其次,便要配置虚拟站点,因为当项目多的时候,每个项目分配成一个站点,对于开发来说,很方便管理.其实网 ...
- 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】
前言 本人一直开发Android应用,目前Android就业形势恶劣,甚至会一路下滑,因此决定学习服务器开发.采用的语言是java,IDE是Intellij,在下载Intellij的同时看到官网很多优 ...
- C#中的foreach语句与枚举器接口(IEnumerator)及其泛型 相关问题
这个问题从<C#高级编程>数组一节中的foreach语句(6.7.2)发现的. 因为示例代码与之前的章节连贯,所以我修改了一下,把自定义类型改为了int int[] bs = { 2, 3 ...
- 获取CPU和内存呢信息
#include <stdio.h> #include <stdlib.h> #include <winsock.h> #pragma comment(lib, & ...
- 初学jQuery之jQuery事件与动画
今天我们就谈谈jquery中的事件和简单动画吧,它们毕竟基础是进阶华丽的根本!! 1.事件 1.window事件 ready 准备就绪 2.鼠标事件 方法 ...