問題總結:
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.通知結束下載
這樣就完成了取消下載任務。