該文章閱讀的SDWebImage的版本為4.3.3。
該類是SDWebImageManager
類的一個應用——預載入:以較低優先順序批量下載影像進行快取,以供未來使用。
1.SDWebImagePrefetcherDelegate協議
/**
當一張圖片已經預載入好的時候會呼叫這個方法
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
複製程式碼
/**
當所有圖片都已經預載入好的時候會呼叫這個方法
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
複製程式碼
2.公共型別定義
/**
定義用於回撥進度情況的block
*/
typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
複製程式碼
/**
定義用於回撥完成情況的block
*/
typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
複製程式碼
3.公共屬性
/**
網路影像管理者物件
*/
@property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;
複製程式碼
/**
最大影像預載入數量,預設為3張
*/
@property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
複製程式碼
/**
影像載入選項,預設為SDWebImageLowPriority,也就是低優先順序
*/
@property (nonatomic, assign) SDWebImageOptions options;
複製程式碼
/**
預載入所在的佇列,預設是主佇列
*/
@property (strong, nonatomic, nonnull) dispatch_queue_t prefetcherQueue;
複製程式碼
/**
代理物件
*/
@property (weak, nonatomic, nullable) id <SDWebImagePrefetcherDelegate> delegate;
複製程式碼
4.公共方法
/**
獲取單例物件
*/
+ (nonnull instancetype)sharedImagePrefetcher;
複製程式碼
/**
以指定的網路影像管理者物件進行初始化
*/
- (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
複製程式碼
/**
預載入的影像url陣列
*/
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls;
複製程式碼
/**
預載入的影像url陣列,並回撥進度和完成情況
*/
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
複製程式碼
/**
取消所有預載入任務
*/
- (void)cancelPrefetching;
複製程式碼
5.類擴充套件屬性
/**
儲存網路影像管理者物件
*/
@property (strong, nonatomic, nonnull) SDWebImageManager *manager;
複製程式碼
/**
儲存預載入的影像url陣列
*/
@property (strong, atomic, nullable) NSArray<NSURL *> *prefetchURLs;
複製程式碼
/**
儲存請求的數量
*/
@property (assign, nonatomic) NSUInteger requestedCount;
複製程式碼
/**
儲存跳過的數量
*/
@property (assign, nonatomic) NSUInteger skippedCount;
複製程式碼
/**
儲存完成的數量
*/
@property (assign, nonatomic) NSUInteger finishedCount;
複製程式碼
/**
儲存預載入開始的時間
*/
@property (assign, nonatomic) NSTimeInterval startedTime;
複製程式碼
/**
儲存完成回撥block
*/
@property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
複製程式碼
/**
儲存程式回撥block
*/
@property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
複製程式碼
6.方法實現
6.1.生命週期方法實現
- (nonnull instancetype)init {
// 建立新的網路影像管理者物件初始化
return [self initWithImageManager:[SDWebImageManager new]];
}
複製程式碼
6.2.私有方法實現
/**
開始預載入指定索引的影像
*/
- (void)startPrefetchingAtIndex:(NSUInteger)index {
// 建立變數儲存當前正在預載入的url
NSURL *currentURL;
// 加鎖
@synchronized(self) {
// 如果想要預載入的索引超過了總共要預載入的數量,就返回不繼續向下執行了
if (index >= self.prefetchURLs.count) return;
// 記錄要預載入的url
currentURL = self.prefetchURLs[index];
// 記錄請求數量
self.requestedCount++;
}
// 呼叫網路影像管理者物件載入影像
[self.manager loadImageWithURL:currentURL options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// 如果沒有完成就返回
if (!finished) return;
// 記錄完成數量
self.finishedCount++;
// 如果設定了進度回撥block就回撥進度情況
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
// 如果影像沒有載入成功就記錄跳過數量
if (!image) {
self.skippedCount++;
}
// 如果代理物件實現了代理方法,就呼叫傳遞完成情況
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
[self.delegate imagePrefetcher:self
didPrefetchURL:currentURL
finishedCount:self.finishedCount
totalCount:self.prefetchURLs.count
];
}
if (self.prefetchURLs.count > self.requestedCount) {
// 如果總共要預載入的數量比請求的數量多
// 主佇列非同步載入
dispatch_async(self.prefetcherQueue, ^{
// 開始預載入下一個影像
[self startPrefetchingAtIndex:self.requestedCount];
});
} else if (self.finishedCount == self.requestedCount) {
// 如果完成的數量等於請求的數量
// 回撥狀態
[self reportStatus];
// 如果設定了完成回撥block就回撥完成情況
if (self.completionBlock) {
self.completionBlock(self.finishedCount, self.skippedCount);
// 置空儲存完成回撥block的屬性
self.completionBlock = nil;
}
// 置空儲存進度回撥block的屬性
self.progressBlock = nil;
}
}];
}
複製程式碼
/**
回撥狀態
*/
- (void)reportStatus {
// 獲取總共要預載入的數量
NSUInteger total = (self.prefetchURLs).count;
// 如果代理物件實現了代理方法,就呼叫傳遞完成情況
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
[self.delegate imagePrefetcher:self
didFinishWithTotalCount:(total - self.skippedCount)
skippedCount:self.skippedCount
];
}
}
複製程式碼
6.3.公共方法實現
+ (nonnull instancetype)sharedImagePrefetcher {
// 獲取單例物件
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
複製程式碼
- (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
if ((self = [super init])) {
// 儲存網路影像管理者物件
_manager = manager;
// 預設為低優先順序
_options = SDWebImageLowPriority;
// 預載入佇列為主佇列
_prefetcherQueue = dispatch_get_main_queue();
// 最大預載入併發數為3
self.maxConcurrentDownloads = 3;
}
return self;
}
複製程式碼
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls {
// 呼叫下面的全能方法
[self prefetchURLs:urls progress:nil completed:nil];
}
複製程式碼
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
// 取消之前的預載入操作
[self cancelPrefetching];
// 獲取當前時間
self.startedTime = CFAbsoluteTimeGetCurrent();
// 獲取要預載入的url
self.prefetchURLs = urls;
// 儲存完成回撥block
self.completionBlock = completionBlock;
// 儲存進度回撥block
self.progressBlock = progressBlock;
if (urls.count == 0) {
// 如果沒有要預載入的url就直接回撥
if (completionBlock) {
completionBlock(0,0);
}
} else {
// 開啟預載入
NSUInteger listCount = self.prefetchURLs.count;
for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
[self startPrefetchingAtIndex:i];
}
}
}
複製程式碼
- (void)cancelPrefetching {
// 加鎖
@synchronized(self) {
// 置空儲存要預載入的url陣列的屬性
self.prefetchURLs = nil;
// 置空儲存跳過數量的屬性
self.skippedCount = 0;
// 置空儲存請求數量的屬性
self.requestedCount = 0;
// 置空儲存完成數量的屬性
self.finishedCount = 0;
}
// 呼叫網路影像管理者物件取消所有操作
[self.manager cancelAll];
}
複製程式碼
6.4.自定義getter/setter方法實現
- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
// 直接設定網路影像管理者物件的最大併發數
self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
}
複製程式碼
- (NSUInteger)maxConcurrentDownloads {
// 直接獲取網路影像管理者物件的最大併發數
return self.manager.imageDownloader.maxConcurrentDownloads;
}
複製程式碼
7.總結
這個類利用前面對影像載入功能的良好封裝,非常簡潔的實現了預載入影像的功能。
原始碼閱讀系列:SDWebImage
原始碼閱讀:SDWebImage(二)——SDWebImageCompat
原始碼閱讀:SDWebImage(三)——NSData+ImageContentType
原始碼閱讀:SDWebImage(四)——SDWebImageCoder
原始碼閱讀:SDWebImage(五)——SDWebImageFrame
原始碼閱讀:SDWebImage(六)——SDWebImageCoderHelper
原始碼閱讀:SDWebImage(七)——SDWebImageImageIOCoder
原始碼閱讀:SDWebImage(八)——SDWebImageGIFCoder
原始碼閱讀:SDWebImage(九)——SDWebImageCodersManager
原始碼閱讀:SDWebImage(十)——SDImageCacheConfig
原始碼閱讀:SDWebImage(十一)——SDImageCache
原始碼閱讀:SDWebImage(十二)——SDWebImageDownloaderOperation
原始碼閱讀:SDWebImage(十三)——SDWebImageDownloader
原始碼閱讀:SDWebImage(十四)——SDWebImageManager
原始碼閱讀:SDWebImage(十五)——SDWebImagePrefetcher
原始碼閱讀:SDWebImage(十六)——SDWebImageTransition
原始碼閱讀:SDWebImage(十七)——UIView+WebCacheOperation
原始碼閱讀:SDWebImage(十八)——UIView+WebCache
原始碼閱讀:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat
原始碼閱讀:SDWebImage(二十)——UIButton+WebCache
原始碼閱讀:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache