SDWebImage 4.x版本源码分析(六)问题总结

问题总结:

1、为什么设置图片前要取消当前的加载任务?
2、dispatch_group_t 在 UIView+WebCache 里的作用是什么?
3、weak-strong dance的用途?
4、#pragma clang diagnostic push
#pragma clang diagnostic ignored "-W相关命令”
//
#pragma clang diagnostic pop 的作用是什么?

5、如何实现取消下载图片任务的?

一、为什么设置图片前要取消当前的加载任务?

如果不取消,那么当tableView滑动的时候,当前cell的imageView会一直去下载图片,然后优先显示下载完成的图片,导致图片错乱。

二、dispatch_group_t 在 UIView+WebCache 里的作用是什么?

1.在FLAnimatedImageView(WebCache)中创建了线程组dispatch_group_t,然后通过key为SDWebImageInternalSetImageGroupKey的字典传入UIView(WebCache)中
— 如果是展示占位图
2.在UIView(WebCache)中设置占位图时,首先获取到是否存在SetImageGroupKey,也就是说是否是FLAnimatedImageView,是就获取到FLAnimatedImageView(WebCache)中传来的group,先enter,然后调用setImageBlock中的方法。
3.在setImageBlock中设置self.image,然后leave。
— 通过url加载到图片后
4.通过url加载到图片后,拿到group,先enter,然后执行setImageBlock。若此时的图片是GIF类型的,首先从GIF图中取出第一张,设为静态海报图像,避免闪烁。其次在全局队列中异步创建FLAnimatedImage,然后在主线程中设置self.animatedImage,并leave group。
5.notify中的block,也就是当group中的所有任务都完成后,调用下[self setNeedsLayout]; - setNeedsLayout 的作用是标记,标记为需要重新布局,异步调用layoutIfNeeds 刷新布局,不利己刷新,但layoutSubviews一定会被调用。

三、weak-strong dance的用途

通过__weak来避免循环引用,weakSelf是附有__weak修饰符的变量,它并不会持有对象,一旦它指向的对象被废弃了,它将自动被赋值为nil。
在多线程情况下,可能weakSelf指向的对象会在 Block 执行前被废弃。
通过__strong来持有weakSelf指向的对象,保证在执行 Block 期间该对象不会被废弃。

四、

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-W相关命令”
//
#pragma clang diagnostic pop 的作用是什么?

忽略clang编译器警告,如下面的代码如果不写会提示 Undeclared selector ‘dynamicSelector’,

//#pragma clang diagnostic push
//#pragma clang diagnostic ignored "-Wundeclared-selector"
    [self performSelector:@selector(dynamicSelector) withObject:nil];
//#pragma clang diagnostic pop

五、如何实现取消下载图片任务的?

1.在UIView(WebCache)里,首先就调用了取消当前的加载任务。

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key
{
    // 从队列中取消进度下载
    SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
    id<SDWebImageOperation> operation;
    @synchronized (self) {
        // 根据key取出当前的操作 (这里取出的operation是实现了SDWebImageOperation协议的 SDWebImageCombinedOperation)
        operation = [operationDictionary objectForKey:key];
    }
    if (operation) {
        //检查是否实现了SDWebImageOperation协议的方法
        if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]){
            [operation cancel];
        }
        @synchronized (self) {
            [operationDictionary removeObjectForKey:key];
        }
    }
}

这里做了两件事,
1.从operationDictionary中取出operation(实际上就是SDWebImageCombinedOperation),
2.判断operation是否实现了SDWebImageOperation协议(SDWebImageCombinedOperation实现了这个协议),调用取消方法,并从operationDictionary中移除key。

2.SDWebImageCombinedOperation里实现了cancel

@implementation SDWebImageCombinedOperation
- (void)cancel {
    @synchronized(self) {
        self.cancelled = YES;
        if (self.cacheOperation) {
            [self.cacheOperation cancel                                           ];
            self.cacheOperation = nil;
        }
        if (self.downloadToken) {
            [self.manager.imageDownloader cancel:self.downloadToken];
        }
        [self.manager safelyRemoveOperationFromRunning:self];
    }
}

<1.cancel掉self.cacheOperation <NSOperation>
<2.manager的imageDownloader cancel掉 self.downloadToken
<3.manager.runningOperation中删掉self

其中第<2点,调用的SDWebImageDownloader 的-cancel:方法

3.SDWebImageDownloader的-cancel:方法

- (void)cancel:(nullable SDWebImageDownloadToken *)token {
    NSURL *url = token.url;
    if (!url) {
        return;
    }
    LOCK(self.operationsLock);
    SDWebImageDownloaderOperation *operation = [self.URLOperations objectForKey:url];
    if (operation) {
        BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
        if (canceled) {
            [self.URLOperations removeObjectForKey:url];
        }
    }
    UNLOCK(self.operationsLock);
}

<<1.从manager的URLOperations中找到 SDWebImageDownloaderOperation < SDWebImageDownloaderOperationInterface,SDWebImageOperation>
<<2.operation 调用cancel token.downloadOperationCancelToken (这个token.downloadOperationCancelToken 就是回调的字典)
<<3.如果需要取消,从URLOperation中删掉url

其中<<2调用的是SDWebImageDownloaderOperation的-cancel:方法

4.SDWebImageDownloaderOperation 删除保存回调的字典

- (BOOL)cancel:(nullable id)token {
    BOOL shouldCancel = NO;
    //同步 阻塞当前队列和当前线程(用的信号量)
    LOCK(self.callbacksLock);
    //删除数组中回调块数组中的token对象,token是key为string,value是block的字典
    //removeObjectIdenticalTo 删掉地址该token的地址,而不是值。
    [self.callbackBlocks removeObjectIdenticalTo:token];
    //判断数组是否为0.则取消下载任务
    if (self.callbackBlocks.count == 0) {
        shouldCancel = YES;
    }
    UNLOCK(self.callbacksLock);
    if (shouldCancel) {
        [self cancel];
    }
    return shouldCancel;
}

<<<1.从callbackBlocks中删掉token(删掉回调的字典)
<<<2.如果Blocks数组为0,取消下载任务,shouldCancel = YES;
<<<3.如果Blocks数组为0,调用 [self cancel];
<<<4.返回shouldCancel

对于<<<3中的[self cancel]; 实际上调用的是SDWebImageDownloaderOperation的cancel方法

5.SDWebImageDownloaderOperation 取消下载任务

- (void)cancel {
    @synchronized (self) {
        [self cancelInternal];
    }
}

- (void)cancelInternal {
    if (self.isFinished) return;
    [super cancel];
    //如果下载图片的任务仍在 则立即取消cancel,并且发送结束下载的通知
    if (self.dataTask) {
        [self.dataTask cancel];
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
        });
        // As we cancelled the task, its callback won't be called and thus won't
        // maintain the isFinished and isExecuting flags.
        if (self.isExecuting) self.executing = NO;
        if (!self.isFinished) self.finished = YES;
    }
    [self reset];
}

<<<<1.super cancel
<<<<2.如果还有任务,NSURLSessionTask cancel
<<<<3.通知结束下载

这样就完成了取消下载任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容