YYWebImageManager.m 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. //
  2. // YYWebImageManager.m
  3. // YYWebImage <https://github.com/ibireme/YYWebImage>
  4. //
  5. // Created by ibireme on 15/2/19.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYWebImageManager.h"
  12. #import "YYImageCache.h"
  13. #import "YYWebImageOperation.h"
  14. #import "YYImageCoder.h"
  15. #import <objc/runtime.h>
  16. #define kNetworkIndicatorDelay (1/30.0)
  17. /// Returns nil in App Extension.
  18. static UIApplication *_YYSharedApplication() {
  19. static BOOL isAppExtension = NO;
  20. static dispatch_once_t onceToken;
  21. dispatch_once(&onceToken, ^{
  22. Class cls = NSClassFromString(@"UIApplication");
  23. if(!cls || ![cls respondsToSelector:@selector(sharedApplication)]) isAppExtension = YES;
  24. if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) isAppExtension = YES;
  25. });
  26. #pragma clang diagnostic push
  27. #pragma clang diagnostic ignored "-Wundeclared-selector"
  28. return isAppExtension ? nil : [UIApplication performSelector:@selector(sharedApplication)];
  29. #pragma clang diagnostic pop
  30. }
  31. @interface _YYWebImageApplicationNetworkIndicatorInfo : NSObject
  32. @property (nonatomic, assign) NSInteger count;
  33. @property (nonatomic, strong) NSTimer *timer;
  34. @end
  35. @implementation _YYWebImageApplicationNetworkIndicatorInfo
  36. @end
  37. @implementation YYWebImageManager
  38. + (instancetype)sharedManager {
  39. static YYWebImageManager *manager;
  40. static dispatch_once_t onceToken;
  41. dispatch_once(&onceToken, ^{
  42. YYImageCache *cache = [YYImageCache sharedCache];
  43. NSOperationQueue *queue = [NSOperationQueue new];
  44. if ([queue respondsToSelector:@selector(setQualityOfService:)]) {
  45. queue.qualityOfService = NSQualityOfServiceBackground;
  46. }
  47. manager = [[self alloc] initWithCache:cache queue:queue];
  48. });
  49. return manager;
  50. }
  51. - (instancetype)init {
  52. @throw [NSException exceptionWithName:@"YYWebImageManager init error" reason:@"Use the designated initializer to init." userInfo:nil];
  53. return [self initWithCache:nil queue:nil];
  54. }
  55. - (instancetype)initWithCache:(YYImageCache *)cache queue:(NSOperationQueue *)queue{
  56. self = [super init];
  57. if (!self) return nil;
  58. _cache = cache;
  59. _queue = queue;
  60. _timeout = 15.0;
  61. if (YYImageWebPAvailable()) {
  62. _headers = @{ @"Accept" : @"image/webp,image/*;q=0.8" };
  63. } else {
  64. _headers = @{ @"Accept" : @"image/*;q=0.8" };
  65. }
  66. return self;
  67. }
  68. - (YYWebImageOperation *)requestImageWithURL:(NSURL *)url
  69. options:(YYWebImageOptions)options
  70. progress:(YYWebImageProgressBlock)progress
  71. transform:(YYWebImageTransformBlock)transform
  72. completion:(YYWebImageCompletionBlock)completion {
  73. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  74. request.timeoutInterval = _timeout;
  75. request.HTTPShouldHandleCookies = (options & YYWebImageOptionHandleCookies) != 0;
  76. request.allHTTPHeaderFields = [self headersForURL:url];
  77. request.HTTPShouldUsePipelining = YES;
  78. request.cachePolicy = (options & YYWebImageOptionUseNSURLCache) ?
  79. NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
  80. YYWebImageOperation *operation = [[YYWebImageOperation alloc] initWithRequest:request
  81. options:options
  82. cache:_cache
  83. cacheKey:[self cacheKeyForURL:url]
  84. progress:progress
  85. transform:transform ? transform : _sharedTransformBlock
  86. completion:completion];
  87. if (_username && _password) {
  88. operation.credential = [NSURLCredential credentialWithUser:_username password:_password persistence:NSURLCredentialPersistenceForSession];
  89. }
  90. if (operation) {
  91. NSOperationQueue *queue = _queue;
  92. if (queue) {
  93. [queue addOperation:operation];
  94. } else {
  95. [operation start];
  96. }
  97. }
  98. return operation;
  99. }
  100. - (NSDictionary *)headersForURL:(NSURL *)url {
  101. if (!url) return nil;
  102. return _headersFilter ? _headersFilter(url, _headers) : _headers;
  103. }
  104. - (NSString *)cacheKeyForURL:(NSURL *)url {
  105. if (!url) return nil;
  106. return _cacheKeyFilter ? _cacheKeyFilter(url) : url.absoluteString;
  107. }
  108. #pragma mark Network Indicator
  109. + (_YYWebImageApplicationNetworkIndicatorInfo *)_networkIndicatorInfo {
  110. return objc_getAssociatedObject(self, @selector(_networkIndicatorInfo));
  111. }
  112. + (void)_setNetworkIndicatorInfo:(_YYWebImageApplicationNetworkIndicatorInfo *)info {
  113. objc_setAssociatedObject(self, @selector(_networkIndicatorInfo), info, OBJC_ASSOCIATION_RETAIN);
  114. }
  115. + (void)_delaySetActivity:(NSTimer *)timer {
  116. UIApplication *app = _YYSharedApplication();
  117. if (!app) return;
  118. NSNumber *visiable = timer.userInfo;
  119. if (app.networkActivityIndicatorVisible != visiable.boolValue) {
  120. [app setNetworkActivityIndicatorVisible:visiable.boolValue];
  121. }
  122. [timer invalidate];
  123. }
  124. + (void)_changeNetworkActivityCount:(NSInteger)delta {
  125. if (!_YYSharedApplication()) return;
  126. void (^block)() = ^{
  127. _YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
  128. if (!info) {
  129. info = [_YYWebImageApplicationNetworkIndicatorInfo new];
  130. [self _setNetworkIndicatorInfo:info];
  131. }
  132. NSInteger count = info.count;
  133. count += delta;
  134. info.count = count;
  135. [info.timer invalidate];
  136. info.timer = [NSTimer timerWithTimeInterval:kNetworkIndicatorDelay target:self selector:@selector(_delaySetActivity:) userInfo:@(info.count > 0) repeats:NO];
  137. [[NSRunLoop mainRunLoop] addTimer:info.timer forMode:NSRunLoopCommonModes];
  138. };
  139. if ([NSThread isMainThread]) {
  140. block();
  141. } else {
  142. dispatch_async(dispatch_get_main_queue(), block);
  143. }
  144. }
  145. + (void)incrementNetworkActivityCount {
  146. [self _changeNetworkActivityCount:1];
  147. }
  148. + (void)decrementNetworkActivityCount {
  149. [self _changeNetworkActivityCount:-1];
  150. }
  151. + (NSInteger)currentNetworkActivityCount {
  152. _YYWebImageApplicationNetworkIndicatorInfo *info = [self _networkIndicatorInfo];
  153. return info.count;
  154. }
  155. @end