美文网首页
iOS学习-多线程4

iOS学习-多线程4

作者: 快乐的tomato | 来源:发表于2021-09-15 12:42 被阅读0次

    一、SDWebImage第三方框架

    1、是什么

    一个为UIImageView提供一个分类来支持远程服务器图片加载的库。
    功能:

          1、一个添加了web图片加载和缓存管理的UIImageView分类
          2、一个异步图片下载器
          3、一个异步的内存加磁盘综合存储图片并且自动处理过期图片
          4、支持动态gif图
          5、支持webP格式的图片
          6、后台图片解压处理
          7、确保同样的图片url不会下载多次
          8、确保伪造的图片url不会重复尝试下载
          9、确保主线程不会阻塞
    

    2、简单使用

    #import "UIImageView+WebCache.h"
    
    UIImageView *image = [[UIImageView alloc] init];
    [image sd_setImageWithURL:[NSURL URLWithString:@"图片地址"] placeholderImage:[UIImage imageNamed:@"站位图"]];
    

    3、工作流程

    1、入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
    
    2、进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
    
    3、先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
    
    4、SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
    
    5、如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
    
    6、根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
    
    7、如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
    
    8、如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
    
    9、共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
    
    10、图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
    
    11、connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
    
    12、图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
    
    13、在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
    
    14、通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
    
    15、SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
    
    16、SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
    
    17、SDWebImagePrefetcher 可以预先下载图片,方便后续使用
    

    参考:
    [SDWebImage原理](https://www.jianshu.com/p/ff9095de1753?utm_source=desktop&utm_medium=timelin

    二、死锁问题

    1、什么是死锁?

    线程卡住,不会往下执行

    2、什么情况下会产生死锁?

    使用sync函数往当前串行队列中添加任务,会卡住当前的串行对列,产生死锁,其他的基本不会。

    • 主队列的特点:
      先执行完主线程上的代码,才会执行主对列中的任务
    • dispatch_sync的特点
      立马在当前线程执行任务
     NSLog(@"任务1");
        //对列的特点:排队,FIFO,先进先出
        dispatch_sync(dispatch_get_main_queue(), ^{
            
            NSLog(@"任务2");
        });
        NSLog(@"任务2");
    

    上面这段代码会崩掉,
    因为根据主队列和dispatch_sync的特点,任务2和任务3会造成相互等待而不能动的情况,产生死锁。

    三、相关面试题

    1、你理解的多线程?
    2、下面打印的结果是什么?

    dispatch_queue_t queue = dispatch_queue_create("hahaha", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            
             NSLog(@"任务1==%@",[NSThread currentThread]);
            [self performSelector:@selector(Test) withObject:nil afterDelay:.0];
             NSLog(@"任务3==%@",[NSThread currentThread]);
    
           });
    
    -(void)Test{
        NSLog(@"任务2==%@",[NSThread currentThread]);
    }
    
    
    2021-09-16 16:34:00.814177+0800 OCStudy[32750:3081015] 任务1==<NSThread: 0x6000009dda00>{number = 6, name = (null)}
    2021-09-16 16:34:00.814845+0800 OCStudy[32750:3081015] 任务3==<NSThread: 0x6000009dda00>{number = 6, name = (null)}
    
    

    结果只打印了1和3。

    [self performSelector:@selector(Test) withObject:nil afterDelay:.0];
    换成
    [self performSelector:@selector(Test) withObject:nil];  //等价于   [self Test];
    

    结果只打印1、2、3。
    原因:
    [self performSelector:@selector(Test) withObject:nil afterDelay:.0]
    这句代码本质是往runloop里面添加了定时器,定时器只有在runloop中才能起作用,而子线程默认是没有启动runloop的,所以不起作用。

     dispatch_async(queue, ^{
            
             NSLog(@"任务1==%@",[NSThread currentThread]);
            [self performSelector:@selector(Test) withObject:nil afterDelay:.2];
             NSLog(@"任务3==%@",[NSThread currentThread]);
    
            //添加runloop
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            
           });
    

    添加runloop即可。

    相关文章

      网友评论

          本文标题:iOS学习-多线程4

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