EMSDWebImagePrefetcher.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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 "EMSDWebImagePrefetcher.h"
  9. #if !defined(DEBUG) && !defined (SD_VERBOSE)
  10. #define NSLog(...)
  11. #endif
  12. @interface EMSDWebImagePrefetcher ()
  13. @property (strong, nonatomic) EMSDWebImageManager *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) EMSDWebImagePrefetcherCompletionBlock completionBlock;
  20. @property (copy, nonatomic) EMSDWebImagePrefetcherProgressBlock progressBlock;
  21. @end
  22. @implementation EMSDWebImagePrefetcher
  23. + (EMSDWebImagePrefetcher *)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 = [EMSDWebImageManager new];
  34. _options = EMSDWebImageLowPriority;
  35. self.maxConcurrentDownloads = 3;
  36. }
  37. return self;
  38. }
  39. - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
  40. self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
  41. }
  42. - (NSUInteger)maxConcurrentDownloads {
  43. return self.manager.imageDownloader.maxConcurrentDownloads;
  44. }
  45. - (void)startPrefetchingAtIndex:(NSUInteger)index {
  46. if (index >= self.prefetchURLs.count) return;
  47. self.requestedCount++;
  48. [self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, EMSDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
  49. if (!finished) return;
  50. self.finishedCount++;
  51. if (image) {
  52. if (self.progressBlock) {
  53. self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
  54. }
  55. NSLog(@"Prefetched %@ out of %@", @(self.finishedCount), @(self.prefetchURLs.count));
  56. }
  57. else {
  58. if (self.progressBlock) {
  59. self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
  60. }
  61. NSLog(@"Prefetched %@ out of %@ (Failed)", @(self.finishedCount), @(self.prefetchURLs.count));
  62. // Add last failed
  63. self.skippedCount++;
  64. }
  65. if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
  66. [self.delegate imagePrefetcher:self
  67. didPrefetchURL:self.prefetchURLs[index]
  68. finishedCount:self.finishedCount
  69. totalCount:self.prefetchURLs.count
  70. ];
  71. }
  72. if (self.prefetchURLs.count > self.requestedCount) {
  73. dispatch_async(dispatch_get_main_queue(), ^{
  74. [self startPrefetchingAtIndex:self.requestedCount];
  75. });
  76. }
  77. else if (self.finishedCount == self.requestedCount) {
  78. [self reportStatus];
  79. if (self.completionBlock) {
  80. self.completionBlock(self.finishedCount, self.skippedCount);
  81. self.completionBlock = nil;
  82. }
  83. }
  84. }];
  85. }
  86. - (void)reportStatus {
  87. NSUInteger total = [self.prefetchURLs count];
  88. NSLog(@"Finished prefetching (%@ successful, %@ skipped, timeElasped %.2f)", @(total - self.skippedCount), @(self.skippedCount), CFAbsoluteTimeGetCurrent() - self.startedTime);
  89. if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
  90. [self.delegate imagePrefetcher:self
  91. didFinishWithTotalCount:(total - self.skippedCount)
  92. skippedCount:self.skippedCount
  93. ];
  94. }
  95. }
  96. - (void)prefetchURLs:(NSArray *)urls {
  97. [self prefetchURLs:urls progress:nil completed:nil];
  98. }
  99. - (void)prefetchURLs:(NSArray *)urls progress:(EMSDWebImagePrefetcherProgressBlock)progressBlock completed:(EMSDWebImagePrefetcherCompletionBlock)completionBlock {
  100. [self cancelPrefetching]; // Prevent duplicate prefetch request
  101. self.startedTime = CFAbsoluteTimeGetCurrent();
  102. self.prefetchURLs = urls;
  103. self.completionBlock = completionBlock;
  104. self.progressBlock = progressBlock;
  105. // Starts prefetching from the very first image on the list with the max allowed concurrency
  106. NSUInteger listCount = self.prefetchURLs.count;
  107. for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
  108. [self startPrefetchingAtIndex:i];
  109. }
  110. }
  111. - (void)cancelPrefetching {
  112. self.prefetchURLs = nil;
  113. self.skippedCount = 0;
  114. self.requestedCount = 0;
  115. self.finishedCount = 0;
  116. [self.manager cancelAll];
  117. }
  118. @end