美文网首页收藏ios
iOS 异步加载本地图片

iOS 异步加载本地图片

作者: FGNeverMore | 来源:发表于2019-01-28 17:46 被阅读6次

    iOS 异步加载本地图片

    问题

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

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

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

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

    方案1

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

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


      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,希望不吝赐教。

    相关文章

      网友评论

        本文标题:iOS 异步加载本地图片

        本文链接:https://www.haomeiwen.com/subject/doxzjqtx.html