原始碼閱讀:SDWebImage(十五)——SDWebImagePrefetcher

堯少羽發表於2018-07-06

該文章閱讀的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(一)——從使用入手

原始碼閱讀: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

相關文章