imageWithContentsOfFile则仅只加载图片,不缓存.大量使用imageNamed方式会在不需要缓存的地方额外增加开销CPU的时间来做这件事.当应用程序需要加载一张比较大的图片并且使用一次性,那么其实是没有必要去缓存这个图片的,用imageWithContentsOfFile是最为经济的方式,这样不会因为UIImage元素较多情况下,CPU会被逐个分散在不必要缓存上浪费过多时间.。
LaunchImage是app的启动页(快闪页),不同的手机加载同一张图片会出现拉伸等现象,因此要做相应适配
声明:只做了iphone手机的适配
战前准备,以下图片各一张
1242 px x 2688 px、828 x 1792、1125 × 2436、1242 x 2208、750 x 1334、640 x 960、640 x 1136
开始表演
1.选中LaunchScreen.storyboard, 勾选掉use as launch screen
2.选中工程,清除掉Launch Screen File中的路径,选中Launch Image Sourc
3.添加已经准备好的图片到assets.xcassets中的LaunchImage(如果没有,右击空白处,选中App Icons&Launch Images,选择New iOS Launch Image)
至此设配就完成了
补充:在app中想直接读取LaunchImage,是没法直接使用用户自己设置的名称来读取的,苹果做了相应的和转化,在查看包内容的时候可以查看到。
判断不同的机型, 宏定义
NSString*LaunchImage =@""
if (IS_iPhoneXS_MAX){
LaunchImage =@"LaunchImage-1200-Portrait-2688h@3x"
} else if (IS_iPhoneXR) {
LaunchImage =@"LaunchImage-1200-Portrait-1792h@2x"
} else if (IS_iPhoneX) {
LaunchImage =@"LaunchImage-1100-Portrait-2436h@3x"
} else if (IS_iPhone6_Plus) {
LaunchImage =@"LaunchImage-800-Portrait-736h@3x"
} else if (IS_iPhone_6) {
LaunchImage =@"LaunchImage-800-667h@2x"
} else if (IS_iPhone_5) {
LaunchImage =@"LaunchImage-700-568h@2x"
} else if (IS_iPhone_4S) {
LaunchImage =@"LaunchImage-700@2x"
}
return LaunchImage
在iOS开发中, 图片(UIImage) 是我们在开发中,占用手机 内存 比较大的对象,如果在运行过程中,内存占用过大,对 电池寿命 会造成影响,如果超过了 内存占用的最大值 ,会造成App的 crash 。这篇文章从 图片的加载 原理和 SDWebImage 的源码实现的角度来介绍图片加载。
在iOS中使用 UIImage 和 UIImageView 来记载图片,他俩遵守经典的 MVC 架构, UIImage 相当于 Model , UIImageView 相当于 View :
UIImage 负责 加载图片 , UIImageView 负责 渲染图片 。
图片的渲染流程分为 3个阶段 : 加载(Load),解码(Decoder)和渲染(Render)
在每个阶段都会有相对应的 缓冲区 : 数据缓冲区(DataBuffer),图像缓冲区(imageBuffer)和帧缓冲区(framebuffer) 。
我们以加载一个图片的尺寸为: 2048 px * 1536 px ,在磁盘上的大小为: 590kb 的图片为例,来分析前两个阶段的缓冲区。
DataBuffer 只是一种包含 一系列字节 的缓冲区。通常以某些 元数据 开头, 元数据 描述了存储在数据缓冲区中的图像大小,包含图形数据本身, 图像数据以某种形式编码 如 JPEG压缩或PNG,这意味着, 该字节并不直接描述图像中像素的任何内容 。此时的 DataBuffer 大小为 590kb 。
在 SDWebImage 中,图片加载完成后,在 sd_imageFormatForImageData 的方法中,是通过 DataBuffer 的 第一个字节 来判断图片的格式的。
在 图片加载 完后,需要将 Data Buffer 的 JPEG,PNG或其他编码的数据 ,转换为 每个像素 的 图像信息 ,这个过程,称为 Decoder(解码) ,将 像素信息 存放在 ImageBuffer 。
图片占用的内存大小与 图像的尺寸有关 ,与它的 文件大小无关 ,在iOS SRGB 显示格式中 (4byte空间显示一个像素) ,如果解析所有的像素,需要 2048 px * 1536 px * 4 byte/px = 10MB 的空间,此时的 ImageBuffer 的大小为 10MB 。
在 ImageBuffer 解析完后,提交给 frameBuffer 进行渲染显示。
总的来说,图片加载过程和消耗的内存如下图所示:
在 Xcode 工程中,当push新页面的时候,只加载一个图片。
加载前内存值:
加载后内存值:
大多数情况下,我们并不需要如此高精度的显示图片,占用了这么多的内存,能否减少加载图片时占用的内存值呢?
在苹果官方文档中,建议我们使用 向下采样(Downsampleing) 的技术,来加载图片,减少 ImageBuffer 的大小。
方法如下:
我们来测试一下:
加载之前时是 13M ,加载之后是 17M ,效果是很明显的,节省了大约 5M 的内存空间。
在对图片进行压缩时,我们应首选向下采样技术 。
在 SDWebIamge 中,一共有3种类型的解码器: SDImageIOCoder, SDImageGIFCoder, SDImageAPNGCoder ,根据 DataBuffer 的编码类型,使用相对应的编码器。
在 -(UIImage *)decodedImageWithData:(NSData *)data 方法中,配置解码参数,开始进行解码 *** 作。
在 + (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize options:(NSDictionary )options 中,完成图像解码
在 iOS中,渲染图片格式有4种
正确的思路是: 不选择渲染格式,让渲染格式选择你 。
使用 UIGraphicsImageRender 来替换 UIGraphicsBeginImageContextWithOptions ,前者在 iOS12 以后,会自动选择渲染格式,后者默认都会选择 SRGB Format 。
此时,系统为自动选择 Alpha 8 Format 格式,内容空间占用,将会减少 75% 。
在需要绘制带有子视图的View时,不使用 draw(rect:) 方法,使用 系统的View属性 或者 添加子视图 的方式,将绘制工作交给系统来处理。
背景色直接通过 UIView.backgroundColor 设置,而非使用 draw(rect:)
我们在开发中,一般会对图片进行 子线程异步加载 ,在后台进行 解码和下采样 。在列表中,有时会加载很多图片,此时应该注意 线程爆炸 问题。
当我们要求 系统去做比CPU能够做的工作更多的工作时 就会发生这种情况,比如我们要显示 8张图片 ,但我们只有 两个CPU ,就不能一次完成所有这些工作,无法在不存在的CPU上进行并行处理, 为了避免向一个全局队列中异步的分配任务时发生死锁 , GCD 将创建新线程来捕捉我们要求它所做的工作,然后CPU将花费大量时间,在这些 线程 之间进行 切换 ,尝试在所有工作上取得我们要求 *** 作系统为我们做的 渐进式进展 ,在这些线程之间 不停切换 ,实际上是相当大的开销,现在 不是简单地将工作分派到全局异步队列之一 ,而是 创建一个串行队列 ,在预取的方法中,异步的将工作分派到该队列,它的确意味着单个图像的加载,可能要比以前晚才能开始取得进展,但CPU将花费更少的时间,在它可以做的小任务之间来回切换。
在 SDWebImage 中,解码的队列 _coderQueue.maxConcurrentOperationCount = 1 就是一个串行队列。这样就很好的解决了 多图片异步解码 时, 线程爆炸 问题。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)