123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /*
- * This file is part of the SDWebImage package.
- * (c) Olivier Poitrey <rs@dailymotion.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- #import "SDWebImagePrefetcher.h"
- @interface SDWebImagePrefetcher ()
- @property (strong, nonatomic, nonnull) SDWebImageManager *manager;
- @property (strong, atomic, nullable) NSArray<NSURL *> *prefetchURLs; // may be accessed from different queue
- @property (assign, nonatomic) NSUInteger requestedCount;
- @property (assign, nonatomic) NSUInteger skippedCount;
- @property (assign, nonatomic) NSUInteger finishedCount;
- @property (assign, nonatomic) NSTimeInterval startedTime;
- @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
- @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
- @end
- @implementation SDWebImagePrefetcher
- + (nonnull instancetype)sharedImagePrefetcher {
- static dispatch_once_t once;
- static id instance;
- dispatch_once(&once, ^{
- instance = [self new];
- });
- return instance;
- }
- - (nonnull instancetype)init {
- return [self initWithImageManager:[SDWebImageManager new]];
- }
- - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
- if ((self = [super init])) {
- _manager = manager;
- _options = SDWebImageLowPriority;
- _prefetcherQueue = dispatch_get_main_queue();
- self.maxConcurrentDownloads = 3;
- }
- return self;
- }
- - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
- self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
- }
- - (NSUInteger)maxConcurrentDownloads {
- return self.manager.imageDownloader.maxConcurrentDownloads;
- }
- - (void)startPrefetchingAtIndex:(NSUInteger)index {
- NSURL *currentURL;
- @synchronized(self) {
- if (index >= self.prefetchURLs.count) return;
- 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++;
- if (self.progressBlock) {
- self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
- }
- if (!image) {
- // Add last failed
- 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, ^{
- // we need dispatch to avoid function recursion call. This can prevent stack overflow even for huge urls list
- [self startPrefetchingAtIndex:self.requestedCount];
- });
- } else if (self.finishedCount == self.requestedCount) {
- [self reportStatus];
- if (self.completionBlock) {
- self.completionBlock(self.finishedCount, self.skippedCount);
- self.completionBlock = nil;
- }
- 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
- ];
- }
- }
- - (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]; // Prevent duplicate prefetch request
- self.startedTime = CFAbsoluteTimeGetCurrent();
- self.prefetchURLs = urls;
- self.completionBlock = completionBlock;
- self.progressBlock = progressBlock;
- if (urls.count == 0) {
- if (completionBlock) {
- completionBlock(0,0);
- }
- } else {
- // Starts prefetching from the very first image on the list with the max allowed concurrency
- NSUInteger listCount = self.prefetchURLs.count;
- for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
- [self startPrefetchingAtIndex:i];
- }
- }
- }
- - (void)cancelPrefetching {
- @synchronized(self) {
- self.prefetchURLs = nil;
- self.skippedCount = 0;
- self.requestedCount = 0;
- self.finishedCount = 0;
- }
- [self.manager cancelAll];
- }
- @end
|