WWDC18 - 图形图像最佳实践(一)

上周四收到了Apple发来的邮件说是WWDC18视频配有中文字幕了,其实6月份就看过一部分,但全英文无任何字幕看起来略(马)显(拉)吃(个)力(币),所以看到有字幕迫不及待,把自己感兴趣的几个Session看了一遍,虽然并没有发现有中文字幕,但有英文字幕也起比之前顺畅多了,好了进入正题分享下 WWDC 18 Session 219 :Image and Graphics Best Practices ,图形图像最佳实践,此Session由Kyle Sluder分享。

640?wx_fmt=png

问题

我还是先抛个问题吧,下图渲染后占用多大内存?没错,这个问题今天不讲!这个知识点在 WWDC 18 Session 416 iOS Memory Deep Dive有较深入的探讨,挺有趣的。

640?wx_fmt=png

 UIImage and UIImageView

1.1 渲染的过程

640?wx_fmt=png

在Cocoa的MVC架构中,UIImage扮演Model的角色,UIImageView则充当View的角色,UIImage用于加载图片内容,UIImageView负责将图片内容渲染和显示,一个Image在屏幕中显示流程如下图所示。但在讨论Decode(解码)环节前,我们得先明确Buffers的概念。640?wx_fmt=png

1.2 Buffers

Image Buffer中的每一个元素描述的是一个像素的颜色信息,Buffer的size与图片的size为正相关关系。Image Buffer的一个典型例子便是framebuffer,framebuffer真正管理着屏幕的输出。

640?wx_fmt=png

640?wx_fmt=png

在应用程序更新图层时,UIKit将window及其subviews渲染至frame buffer,这个frame buffer提供每个像素的信息以供显示硬件定时读取,读取的频率一般为60Hz,但在ipad pro上可提升至120Hz。 与Image Buffer不同,Data Buffer存储着一个图片文件的内容,通常为JPG,PNG等压缩格式,因此其内容不是直接与像素的信息一一对应。metadata存储着图片的尺寸。

640?wx_fmt=png

1.3 Decode过程

640?wx_fmt=png

理解了buffers的概念后,我们接着分析Decode环节的。如上图所示,在实际的渲染过程中,UIImage负责解压Data Buffer内容并申请buffer(Image Buffer)存储解码后的图片信息,然后UIImageView负责将Image Buffer 拷贝至 framebuffer,用于给显示硬件提供颜色信息。

解压过程是一个大量占用CPU资源的工作,因此UIImage 会持有解码后的Image Buffer以便给渲染工作提供信息,Image Buffer与图片的实际尺寸有关,与图片文件大小无关。若是在TableView等列表中连续加载多张图片,便会引发连续的大块内存分配,这将对Memory和CPU带来沉重的负担。

640?wx_fmt=png

1.4 DownSampling

640?wx_fmt=png

DownSampling即在Decode前插入创建缩略图的过程。如下图所示,即对image进行预处理,采用缩小解码后Image Buffer的size的方式,减少内存的占用。此处需注意两个选项的设置。 

640?wx_fmt=png

经过此预处理图片后,内存占用的前后对比如下,减少大约50%的内存占用率: 

640?wx_fmt=png

但正如Decode部分的介绍,解码是一个CPU密集型工作,如果在主线程中做同步解压,会导致主线程卡顿,影响用户的使用体验(感觉Apple爸爸啰嗦了,iOSTips的读者朋友应该不会在主线程解码)。因此使用Downsampling的正确姿势如下: 640?wx_fmt=png

此处通过两种手段解决主线程卡顿的问题: 

640?wx_fmt=png

1.5 Image Source

推荐使用Image Assets,主要有如下几点原因: 

Custom drawing with UIKit

2.1 Custom drawing

以如下live按钮的实现为例: 640?wx_fmt=png

例子中提供了使用draw(_ rect: CGRect)的实现方案,代码如下: 640?wx_fmt=png

在说明这种解决方案的优劣前,我们先来说明重写draw:函数后,系统渲染的过程: 640?wx_fmt=png

我们知道,UIView的CALayer负责将contents传递给frame buffer,当重写draw:函数后,CALayer会负责创建一个Backing Store(尺寸与view显示的size成正相关),然后在此Backing store上执行draw:函数,最后将其中的内容传递给frame buffer。

减少Backing Store的使用,减少Backing Store的使用,减少Backing Store的使用(三遍说明重要)。针对live按钮的实现,我们应该将大的view化为多个subview的组合,减少draw:函数的使用减少imageData的多重拷贝(在给出的解决方案中,会将image ‘draw at’ Backing store,多了一次imageData的拷贝动作),使用优化的选项和属性(UIView的backgroundColor属性可以不通过Backing store直接渲染至framebuffer。但pattern color例外,可用添加imageview为subview代替)。

2.2 背景的实现

UIView的maskView 及CALayer.maskLayer都会将图层渲染到临时的image buffer中,而CALayer.cornerRadius不需要。对于live按钮的背景实现,不要用maskView,以UIImageView+Resizable的方式解决。 640?wx_fmt=png

2.3 图片的实现

Tips:UIImageView在渲染时可直接对单色图片着色,设置tint color来改变单色图片的颜色从而达到复用图片的目的。 640?wx_fmt=png

2.4 文字的实现

Tips:UILabel对单色的string做了优化处理,可节省75%的Backing Store,并能自动更新Backing Store的size以适配富文本或emoji。 640?wx_fmt=png

3. Advanced CPU and GPU

此sessions对这一部分仅做了简单的描述,篇幅限制不做分享。

最后WWDC都挺有趣的,建议各位可以抽时间研究研究,接下来我应该也还会对其他Sessions做一些脱水分享。

推荐阅读:

  1. Image and Graphics Best Practices  

    https://developer.apple.com/videos/play/wwdc2018/219/

  2. iOS Memory Deep Dive https://developer.apple.com/videos/play/wwdc2018/416

  3.   iOStips,高级进阶电子书干货分享!

PS:公号后台回复关键词【深入理解计算机系统】可获得电子版

640?wx_fmt=png

更多骚操作,尽在iOSTips,关注公众号,第一时间get新姿势。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值