两种利用GCD实现分步获取结果的方式和SDWebImage缓存机制的验证

时间:2023-03-09 07:53:08
两种利用GCD实现分步获取结果的方式和SDWebImage缓存机制的验证

前段时间写界面,因为数据的请求分成了两部分,所以用到了多线程,实现数据的分步请求,然后自己写了一个Demo,用两种方式实现分步获取内容,其中也包含了验证SDWebImage这个库的缓存机制,在这里给大家分享一下,希望对大家有用,不喜勿喷~~

首先想要说的是GCD线程分步实现的过程,代码如下:(相关文字说明在代码中都包含)

#pragma mark --------- 并行异步执行的方法:利用GCD并行多个线程并且等待所有线程结束之后再执行其它方法
- (void)pushRequstData1 {
    __weak typeof (self) selfVc = self;
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
        // 并行执行的线程一
        NSLog(@"11111111");
        [selfVc oneOne];
        NSLog(@".........11111111");
    });
    dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
        // 并行执行的线程二
        NSLog(@"44444444");
        [selfVc twoTwo];
        NSLog(@".........22222222");
    });
    dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
        // 汇总结果
        NSLog(@"这里可以最后刷新数据,更新界面。。。。77777777");
        
    });
}
#pragma mark --------- 串行异步执行的方法:利用GCD串行多个线程,必须等到上一个完成之后,才能执行下一个任务,并且可以等待所有线程结束之后再执行其它方法
- (void)pushRequstData2 {
    __weak typeof (self) selfVc = self;
    //利用GCD串行多个线程,按顺序完成各个任务,并且等待所有线程结束之后再执行其它任务
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_group_async(group, queue1, ^{
        // 串行执行的线程一
        NSLog(@"11111111");
        [selfVc oneOne];
    });
    dispatch_group_async(group, queue1, ^{
        // 串行执行的线程二
        NSLog(@"44444444");
        [selfVc twoTwo];
    });
    dispatch_group_notify(group, queue1, ^{
        // 汇总结果
        NSLog(@"这里可以最后刷新数据,更新界面。。。。77777777");
    });
}

//方法执行
    /*注意:
     1.注意循环引用的问题,如果有用到self,要使用弱引用:__weak typeof (self) selfVc = self;这样效果最好,防止循环引用的问题
     */
    
    
    //[self pushRequstData1];//这个是并行异步的方法,oneOne和twoTwo方法会同时进行,所以执行的顺序不是1234567
    
    [self pushRequstData2];//这个是串行异步执行的方法,会等线程一执行完成,才会执行线程二,所以执行的顺序是1234567

接下来就是验证SDWebImage缓存图片的代码,突发奇想写这个是因为,看到支付宝,爱奇艺等相关APP,在不连接网络的情况下还会显示图片,所以为了实现这一效果和验证缓存机制,写了这些代码,希望对大家有帮助,代码如下:

/**
     验证步骤:
     1:首先连接网络运行APP,并显示图片;
     2:结束APP,退出程序,记得双击home键,把此APP进程删除;
     3:关闭网络,运行APP,并进入显示图片的界面,发现在没有网络的情况下,之前通过网络请求的图片显示了,说明SDWebImage已经把图片下载进行了缓存。
     **/

//添加图片
-(void)addImgV:(NSArray *)arr
{
    if (arr.count<=0) {
        _dataArray = [NSArray arrayWithContentsOfFile:[self saveLocalPath:@"dataStr1"]];
    }else{
        //存入本地,方便没网时,读取缓存在本地的图片文件
        _dataArray = [NSArray arrayWithArray:arr];
        
        [_dataArray writeToFile:[self saveLocalPath:@"dataStr1"] atomically:YES];
    }
    for (int i = 0; i < _dataArray.count; i++) {
        NSDictionary *dic = _dataArray[i];
        UIImageView *imgV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100+i*130, 200, 120)];
        [imgV sd_setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://image.healthmanage.cn/qImage/%@",dic[@"imagePath"]]] placeholderImage:[UIImage imageNamed:@"guanggao.png"]];
        [self.view addSubview:imgV];
    }
}
//缓存到本地的数据方法,强烈建议创建的文件名称使用宏定义方式,以防拼写或读取数据时出错
-(NSString *)saveLocalPath:(NSString *)strPath
{
    NSArray *arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPathStr = arr[0];
    
    NSString *dataPathStr = [documentPathStr stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist",strPath]];
    
    return dataPathStr;
}

SDWebImage的原理:
 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 做了按图片下载进度加载效果。
 
 12、connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
 
 13、图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
 
 14、在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
 
 15、imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
 
 16、通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
 
 17、将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
 
 18、SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
 
 19、SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
 
 20、SDWebImagePrefetcher 可以预先下载图片,方便后续使用。

相关代码,请自行研究,很简单(源码:https://github.com/hbblzjy/OCGCDMoreDispath