UIImageView 性能优化

《TBImageView》

              —–一个异步实现图片添加圆角阴影的框架

1.  从UIImageView的缺陷,来看TBImageView框架的定位

[注:原始图片:无圆角无阴影,不支持透明的jpg图片]

    开始做《淘宝读书》书架和未下载列表的时候,我们通常会直接使用<QuartzCore>来给图片添加阴影和圆角。如:

self.content = [[[UIView allocinitWithFrame:startPostion] autorelease];

self.content.layer.cornerRadius = 10;

self.content.layer.shadowRadius = 10;

self.content.layer.shadowOpacity = 0.8f;

self.content.layer.shadowOffset = CGSizeMake(-3, 3);

self.content.layer.shadowColor = [UIColor blackColor].CGColor;

可惜的是,iOS不支持UIView的圆角和阴影同时作用到一个UIView中,通过代码看到的结果是,没有圆角,只有阴影;只好再添加一行代码:

self.content.layer.masksToBounds = YES

    天啊,圆角有了,阴影没有了。不过,这个难不倒程序员,我们可以有N种方法解决:

1.  UIImageView的后面添加View,让背景View.layer属性设置来显示阴影

2.  UIImageView的后面添加一个大的UIImageView,图片设置为阴影图片

3.  重写drawRect函数,使用CGContext手动绘图

大功告成,使用了额外的一些行代码,完成了我们想要的效果。

 

[注:加工后的图片:圆角+阴影+透明的png图片]

如果使用TBImageView,只需要:

imageView.imageInfo.cornerRadius = 10;

imageView.imageInfo.shadowOffset = CGSizeMake(-3, 3);

imageView.imageInfo.shadowColor = 0xffb66107;

[imageView setImageURL:@"http://img06.taobaocdn.com/bao/uploaded/i6/i1/T1f4emXadaXXcwv_MU_015850.jpg"];

2.  从UIImageView的性能,来看TBImageView框架的改进

iphone4以后Retina屏幕给我们带来视觉的极致粒度,为了保证app的效果,我们不得不使用640×960甚至1536×2048像素来展示图片。在ipad3上阅读类App,一个分页的UIScrollView至少显示一个UIImageView,为了性能和内存,重用UIImageView并把它放到当前显示的区域,即使它只有一个像素进入屏幕区域,你会发现每次进入下一个页面都会有一个明显的延迟,这个延迟来自于将图片从文件解压缩并渲染到屏幕上这一系列工作。而UIImage仅在UIImageView将要显示的时候才会做解压工作。

视图添加和显示必须在主线程中完成,所以UIImage的解压缩和渲染也自然落到了主线程,这就是造成这个延迟的原因。这种情况在UITableView新的row出现的瞬间、在瀑布流新图片的显示、在相册留言中的卡顿等经常遇到。

不幸的是,大多业务型的app的图片,都是从互联网上下载的,看一下一张1024×768的图片从网络传输到显示到手机屏幕上的消耗:

1.  internet传输的时间

2.  保存/从磁盘空间读取

3.  解压缩jpg图片并保存到内存(640x960x4)

4.  将解压缩后的bytes转换成CGContext的时间(改变大小、图层混合等)

    如果是本地图片:

A.  1024×768分辨率90%压缩质量的jpeg图片从加载解压缩到渲染的时间

设备类型

CPU主频

RAM内存

耗时

iPhone3G

400MHz

128M

527ms

iPhone3GS

600MHz

256M

134ms

iPhone4

800MHz

512M

70ms

iPad1

1.0GHz

256M

79ms

iPad2

1.0GHz x 2

512M

51ms

如下图所示

 

[注:图片解码在iOS设备中占用了70%-80%左右时间。绿色是渲染时间,红色是解码时间,蓝色是对象初始化时间]

    绿色部分表示主线程将解码后的图片渲染到屏幕上的时间,红色部分表示解码并转换到CGContext的时间,蓝色部分表示从磁盘空间加载的时间。

    为了缩短图片解码时间,可以使用pngcrush工具优化png图片,解码时间可以提升40%以上,不幸的是,也无法撼动图片解码消耗总时间50%以上的事实。流畅的用户体验,要求动画帧数保持在30帧以上,也就是要求重绘的视图占用主线的时间缩短在40ms以内。

3.  从SDWebImage的优化和layer的重绘机制的碰撞看,TBImageView的改进

添加阴影后的UIImageView的显示区域会增大,因为图片周围有了一个渐变的阴影,如图:

 

[注:加工后的图片四边对称的增加了相等的阴影偏移量,总尺寸响应增加了]

    真实大小就是UIImageView的frame,是否有一张带圆角阴影的图片,真实大小和frame一样,让UIImageView的图片禁止缩放同时居中,那么多出来的实际大小作为阴影显示在frame之外?

实际宽 = (abs(阴影x偏移量) + 模糊半径)x 2 + 图片的宽

实际高 = (abs(阴影y偏移量) + 模糊半径)x 2 + 图片的高

图片在视图中居中显示UIViewContentModeCenter

从网络上下载图片后,在CGContext中添加阴影和圆角,并保存到本地

添加圆角:

CGContextAddArcToPoint(context, fw, fh, fw/2, fh, cornerRadius);

添加阴影:

CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), blur, CGColor);

生成PNG图片

UIImagePNGRepresentation(image);

保存到磁盘空间

[fileManager createFileAtPath:pathname contents:data attributes:nil];

4.  TBImageView的缓存和加载策略

 

[注:最少的占用CPU时间,不阻塞主线程,GCD串行处理任务]

a)    图片缓存策略

采用系统现有的缓存机制NSCache,将强制解码后的图片(位图)缓存,监听内存警告消息,及时清空缓存

[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(clearMemory) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];

b)    图片存储策略

我们会在多个场景使用同一张图片,每个场景会有不同的阴影、圆角,手机流量和网络总是开发者的痛。可以将原图保存下来,同时将处理后的图片保存在另个一文件夹:

如果找到渲染后的图片,就直接显示;如果没有,那就从原始文件夹找到原始图片,重新加工并保存;如果还是没有,就从网络下载图片吧。

c)    图片加载策略

视图对象总是会在任意时间释放掉,就要求我们在dealloc函数中,及时的关闭请求。尽管iOS给我们提供了杀死线程的方法,为了安全,我们给每一个后台任务配置了一个开关,当一个后台图片加载任务,设置为关闭时,它将一个合适的时间停止运行,然后释放掉自己。

加载任务会有多个阶段:从本地获取图片、从网络获取图片、强制解码、渲染图片等等,我们在每个小阶段完成后,检查任务开关,并进行软退出操作和内存清理。

d)    图片显示的优先级调度

通常在低端机上,我们会遇到性能问题,在操作的流畅度和后台加载之间,有没有这种的办法?

iOS给我们提供多种优先级策略,NSURLConnectiond的RunLooper模式为NSEventTrackingRunLoopMode,这种模式下当UIScrollView滚动时,为了保证流畅性,主线程的RunLooper将暂时不处理网络下载请求,我们需要手动的修改NSURLConnection默认的运行模式为:

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

    保证UIScollView滑动的同时下载并加载图片

5.  iOS的图片和阴影的性能优化(概要)

使用QuartzCore库在layer层添加阴影,是一种非常方便常用的做法,在后台,系统将阴影和圆角,在内存中计算好,并显示在屏幕上。

屏幕的每一个像素都是多个视图叠加合并渲染出来的。阴影和圆角是有透明效果的。一个像素中,如果某个视图出现了透明效果,那么会引发这个视图的重绘。不幸的是,iOS给我们提供了便捷的阴影方案,在内存中,阴影效果并没有保存,所以,当视图重绘的时候,它会重新计算阴影完成后,在于当前其他视图做效果叠加。当然,都是主线程中完成的,槽糕的事情就这样发生了。

可以有很多方案减轻主线程压力,可以准备一张带阴影的图片,或者准备一张阴影图片的背景,当然,《TBImageView》框架也许是一种更加好的解决方式。

参考:此文来源


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值