代码
//封装
+ (void)fetchCacheImgs:(NSArray *)arrImgStr finishBlk:(void(^)(NSArray *arrImgs))finishBlk{
NSMutableArray *arrMTemp = [NSMutableArray arrayWithArray:arrImgStr];
__block NSInteger sum = 0;
[arrImgStr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *strImg = obj;
[[SDWebImageManager sharedManager] loadImageWithURL:[NSURL URLWithString:strImg] options:0 progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
sum++;
[arrMTemp replaceObjectAtIndex:idx withObject:image];
if (sum == arrImgStr.count) {
//结束
if (finishBlk) {
finishBlk(arrMTemp);
}
}
}];
}];
}
//调用
[UIImage fetchCacheImgs:@[
@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1558086203027&di=e27944d7d427004a71c2cdb0fbf58125&imgtype=0&src=http%3A%2F%2Fpic139.nipic.com%2Ffile%2F20170819%2F19317778_203257969000_2.jpg",
@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1558086203027&di=83035960a9ac35ee59c90d277e29b484&imgtype=0&src=http%3A%2F%2Fs1.sinaimg.cn%2Fmw690%2F0021szcogy6VoIyTEw8d0%26690",
@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1558086203027&di=5e307af14e64026034b3bf77ac423c21&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fbf389549d64d6853870dbda4e811a0f56ca138aaa424-lj2LaA_fw658",
@"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1792894298,856132472&fm=26&gp=0.jpg"
] finishBlk:^(NSArray *arrImgs) {
self.vm.arrMImgCountImg = [NSMutableArray arrayWithArray:arrImgs];
}];
问题
答案
其实这个和block的机制有关系。block访问外部变量有两种方式。
一种是值传递,另外一种是址传递。
将变量前面加了__block修饰符后就变成了址传递。这时候block内部和外部共享一个变量地址所以可以修改外部变量的值。
还有一种是值传递。就是block会将外部变量的值传递进入block内部,最终用结构体来保存这个值。
让我们来分析下以上代码
首先外部数组遍历,idx=0这时候block将0传入内部利用结构体保存。然后开多线程,再之后idx=1这时候把1传入block内部用结构体保存起来再开一条线程。最终数组遍历完毕,开了多条线程。这时候图片有的数据比较大,有的数据量比较小。所以虽然先开的线程却不一定先请求完数据。所以这里涉及到了多线程排序问题。
比如第二次开的线程图片数据比较小,先请求完毕,先进入block这时候之前传入的idx的值1就起到了作用,block会在内部结构体里去取idx,取的值就是上次传入的值也就是1。我们利用可变数组的特性将对应的字串图片替换成真正的图片即可。
证明
我们改写成for循环,循环变量前边用__block来修饰。让它变成址传递,我们再试试看,会发现结果变了。
我们可以尝试去掉__block修饰符再次运行。
这也从一方面证明了block的特性,值传递,址传递。
另一方面呢可以查看cpp文件,通过查看代码的方式也可以说明这点。
总结
其实以上代码主要是利用SDWebImage的函数loadImageWithURL来完成图片字串转成图片数据。这里会先去缓存区找,找到后直接返回,找不到才去下载。
有点跑题了,从利用SDWebImage完成strImg转Image变成了讲述block的特性。不过我觉得这个地方是值得我们注意的。也是一个容易忽视的知识点。
今天想讲述的主要是block的用法。我们利用了block访问外部变量值的特性来完成了"线程排序"。