iOS 异步加载本地图片

iOS 异步加载本地图片

问题

当某个界面使用系统API + (nullable UIImage *)imageNamed:(NSString *)name;加载了过多本地图片资源时,不可避免的会产生卡顿感。

有过instrumentTime Profiler经验的同学应该有数据上的直观体验,调用imageNamed:首次加载某张图片,往往是最耗时的。

  1. I/O操作:首次加载本地图片时,内存中是没有的,因此去磁盘中加载图片。
  2. 解码:解码图片,将位图数据还原成原始像素数据。

以上两步都是imageNamed:方法在底层做的耗时操作。知道原因,那么方案也就有了,将以上操作放入到子线程中。

方案1

有些同学可能想,在子线程调用imageNamed:先提前加载一次图片,并且系统会帮我们缓存,省事省力省心,美滋滋~但是这里有两个坑:

  1. 线程安全问题,该方法在iOS9以后才线程安全,也就是说iOS9以下版本使用有风险;


    1637319-47640498fad2d40e.jpg
    15486648779770.jpg
  2. 效率问题,将imageNamed:方法放入到子线程调用,使用Time Profiler观察会发现,该方法在子线程耗时比在主线程久的多,猜测为了保证线程安全,底层很多加锁的操作导致;

方案2

那么美滋滋的不行,只能绕一点远路了;

加载图片
// I/O将磁盘数据加载入内存中,NSData类型
+ (nullable instancetype)dataWithContentsOfFile:(NSString *)path;
// 将NSData类型的图片数据转为UIImage类型
- (nullable instancetype)initWithData:(NSData *)data scale:(CGFloat)scale NS_AVAILABLE_IOS(6_0);
解码图片

站在前人肩膀上敲代码很方便,如果你的项目中使用了SDWebImage或YYImage的话,可以直接调用它们框架内部的解码图片方法;

YYImageDecoder类
- (instancetype)imageByDecoded;
SDWebImageDecoder类
+ (UIImage *)decodedImageWithImage:(UIImage *)image;

它们内部逻辑相似:获取imageA对象位图的信息(透明通道、缩放系数、宽高),创建渲染的上下文(CGBitmapContextCreate),将位图渲染在上下文中(CGContextDrawImage),在上下文中提取带有位图信息的imageB对象(CGBitmapContextCreateImage);

缓存

这样子系统就不会帮我们缓存图片了,因为需要我们自己管理缓存图片。
使用YYThreadSafeDictionary线程安全的容器,子线程保存下解码后的图片。

以上几步操作都是在子线程中,降低了主线程的占用,可以有效的解决由于加载本地图片造成的卡顿问题。当在子线程处理完图片后,我们再切换到主线程,将图片扔给使用方。

// 调用接口大致这样
+ (void)asyncLoadImgWithimgName:(NSString *)imgName block:(void(^)(UIImage *image))block;
Github代码 FGAsyncLoadImgUtil

以上就是项目中使用的一个小优化方案,如果同学们其他idea,希望不吝赐教。

猜你喜欢

转载自blog.csdn.net/weixin_34248705/article/details/87415663