SDWebImagePrefetcher.m 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * This file is part of the SDWebImage package.
  3. * (c) Olivier Poitrey <rs@dailymotion.com>
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. */
  8. #import "SDWebImagePrefetcher.h"
  9. #if (!defined(DEBUG) && !defined (SD_VERBOSE)) || defined(SD_LOG_NONE)
  10. #define NSLog(...)
  11. #endif
  12. @interface SDWebImagePrefetcher ()
  13. @property (strong, nonatomic) SDWebImageManager *manager;
  14. @property (strong, nonatomic) NSArray *prefetchURLs;
  15. @property (assign, nonatomic) NSUInteger requestedCount;
  16. @property (assign, nonatomic) NSUInteger skippedCount;
  17. @property (assign, nonatomic) NSUInteger finishedCount;
  18. @property (assign, nonatomic) NSTimeInterval startedTime;
  19. @property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock;
  20. @property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock;
  21. @end
  22. @implementation SDWebImagePrefetcher
  23. + (SDWebImagePrefetcher *)sharedImagePrefetcher {
  24. static dispatch_once_t once;
  25. static id instance;
  26. dispatch_once(&once, ^{
  27. instance = [self new];
  28. });
  29. return instance;
  30. }
  31. - (id)init {
  32. if ((self = [super init])) {
  33. _manager = [SDWebImageManager new];
  34. _options = SDWebImageLowPriority;
  35. _prefetcherQueue = dispatch_get_main_queue();
  36. self.maxConcurrentDownloads = 3;
  37. }
  38. return self;
  39. }
  40. - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
  41. self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
  42. }
  43. - (NSUInteger)maxConcurrentDownloads {
  44. return self.manager.imageDownloader.maxConcurrentDownloads;
  45. }
  46. - (void)startPrefetchingAtIndex:(NSUInteger)index {
  47. if (index >= self.prefetchURLs.count) return;
  48. self.requestedCount++;
  49. [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
  50. if (!finished) return;
  51. self.finishedCount++;
  52. if (image) {
  53. if (self.progressBlock) {
  54. self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
  55. }
  56. NSLog(@"Prefetched %@ out of %@", @(self.finishedCount), @(self.prefetchURLs.count));
  57. }
  58. else {
  59. if (self.progressBlock) {
  60. self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
  61. }
  62. NSLog(@"Prefetched %@ out of %@ (Failed)", @(self.finishedCount), @(self.prefetchURLs.count));
  63. // Add last failed
  64. self.skippedCount++;
  65. }
  66. if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
  67. [self.delegate imagePrefetcher:self
  68. didPrefetchURL:self.prefetchURLs[index]
  69. finishedCount:self.finishedCount
  70. totalCount:self.prefetchURLs.count
  71. ];
  72. }
  73. if (self.prefetchURLs.count > self.requestedCount) {
  74. dispatch_async(self.prefetcherQueue, ^{
  75. [self startPrefetchingAtIndex:self.requestedCount];
  76. });
  77. }
  78. else if (self.finishedCount == self.requestedCount) {
  79. [self reportStatus];
  80. if (self.completionBlock) {
  81. self.completionBlock(self.finishedCount, self.skippedCount);
  82. self.completionBlock = nil;
  83. }
  84. }
  85. }];
  86. }
  87. - (void)reportStatus {
  88. NSUInteger total = [self.prefetchURLs count];
  89. NSLog(@"Finished prefetching (%@ successful, %@ skipped, timeElasped %.2f)", @(total - self.skippedCount), @(self.skippedCount), CFAbsoluteTimeGetCurrent() - self.startedTime);
  90. if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
  91. [self.delegate imagePrefetcher:self
  92. didFinishWithTotalCount:(total - self.skippedCount)
  93. skippedCount:self.skippedCount
  94. ];
  95. }
  96. }
  97. - (void)prefetchURLs:(NSArray *)urls {
  98. [self prefetchURLs:urls progress:nil completed:nil];
  99. }
  100. - (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock {
  101. [self cancelPrefetching]; // Prevent duplicate prefetch request
  102. self.startedTime = CFAbsoluteTimeGetCurrent();
  103. self.prefetchURLs = urls;
  104. self.completionBlock = completionBlock;
  105. self.progressBlock = progressBlock;
  106. if(urls.count == 0){
  107. if(completionBlock){
  108. completionBlock(0,0);
  109. }
  110. }else{
  111. // Starts prefetching from the very first image on the list with the max allowed concurrency
  112. NSUInteger listCount = self.prefetchURLs.count;
  113. for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
  114. [self startPrefetchingAtIndex:i];
  115. }
  116. }
  117. }
  118. - (void)cancelPrefetching {
  119. self.prefetchURLs = nil;
  120. self.skippedCount = 0;
  121. self.requestedCount = 0;
  122. self.finishedCount = 0;
  123. [self.manager cancelAll];
  124. }
  125. @end