@@ -53,7 +53,13 @@ @interface ASMultiplexImageNode ()
5353@private
5454 // Core.
5555 id <ASImageCacheProtocol> _cache;
56+
5657 id <ASImageDownloaderProtocol> _downloader;
58+ struct {
59+ unsigned int downloaderImplementsSetProgress:1 ;
60+ unsigned int downloaderImplementsSetPriority:1 ;
61+ unsigned int downloaderImplementsDownloadWithPriority:1 ;
62+ } _downloaderFlags;
5763
5864 __weak id <ASMultiplexImageNodeDelegate> _delegate;
5965 struct {
@@ -89,8 +95,6 @@ @interface ASMultiplexImageNode ()
8995 BOOL _shouldRenderProgressImages;
9096
9197 // set on init only
92- BOOL _downloaderImplementsSetProgress;
93- BOOL _downloaderImplementsSetPriority;
9498 BOOL _cacheSupportsClearing;
9599}
96100
@@ -171,13 +175,14 @@ - (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASI
171175 _cache = (id <ASImageCacheProtocol>)cache;
172176 _downloader = (id <ASImageDownloaderProtocol>)downloader;
173177
174- _downloaderImplementsSetProgress = [downloader respondsToSelector: @selector (setProgressImageBlock:callbackQueue:withDownloadIdentifier: )];
175- _downloaderImplementsSetPriority = [downloader respondsToSelector: @selector (setPriority:withDownloadIdentifier: )];
178+ _downloaderFlags.downloaderImplementsSetProgress = [downloader respondsToSelector: @selector (setProgressImageBlock:callbackQueue:withDownloadIdentifier: )];
179+ _downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector: @selector (setPriority:withDownloadIdentifier: )];
180+ _downloaderFlags.downloaderImplementsDownloadWithPriority = [downloader respondsToSelector: @selector (downloadImageWithURL:priority:callbackQueue:downloadProgress:completion: )];
176181
177182 _cacheSupportsClearing = [cache respondsToSelector: @selector (clearFetchedImageFromCacheWithURL: )];
178183
179184 _shouldRenderProgressImages = YES ;
180-
185+
181186 self.shouldBypassEnsureDisplay = YES ;
182187
183188 return self;
@@ -271,49 +276,34 @@ - (BOOL)placeholderShouldPersist
271276- (void )displayWillStartAsynchronously : (BOOL )asynchronously
272277{
273278 [super displayWillStartAsynchronously: asynchronously];
274-
275279 [self didEnterPreloadState ];
276-
277- if (_downloaderImplementsSetPriority) {
278- {
279- ASDN::MutexLocker l (_downloadIdentifierLock);
280- if (_downloadIdentifier != nil ) {
281- [_downloader setPriority: ASImageDownloaderPriorityImminent withDownloadIdentifier: _downloadIdentifier];
282- }
283- }
284- }
280+ [self _updatePriorityOnDownloaderIfNeededWithDefaultPriority: ASImageDownloaderPriorityImminent];
285281}
286282
287283/* didEnterVisibleState / didExitVisibleState in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary
288284 in ASNetworkImageNode as well. */
289285- (void )didEnterVisibleState
290286{
291287 [super didEnterVisibleState ];
292-
293- if (_downloaderImplementsSetPriority) {
294- ASDN::MutexLocker l (_downloadIdentifierLock);
295- if (_downloadIdentifier != nil ) {
296- [_downloader setPriority: ASImageDownloaderPriorityVisible withDownloadIdentifier: _downloadIdentifier];
297- }
298- }
299-
288+ [self _updatePriorityOnDownloaderIfNeededWithDefaultPriority: ASImageDownloaderPriorityVisible];
300289 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
301290}
302291
303292- (void )didExitVisibleState
304293{
305294 [super didExitVisibleState ];
306-
307- if (_downloaderImplementsSetPriority) {
308- ASDN::MutexLocker l (_downloadIdentifierLock);
309- if (_downloadIdentifier != nil ) {
310- [_downloader setPriority: ASImageDownloaderPriorityPreload withDownloadIdentifier: _downloadIdentifier];
311- }
312- }
313-
295+ [self _updatePriorityOnDownloaderIfNeededWithDefaultPriority: ASImageDownloaderPriorityPreload];
314296 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
315297}
316298
299+ - (void )didExitDisplayState
300+ {
301+ [super didExitDisplayState ];
302+ if (ASActivateExperimentalFeature (ASExperimentalImageDownloaderPriority)) {
303+ [self _updatePriorityOnDownloaderIfNeededWithDefaultPriority: ASImageDownloaderPriorityPreload];
304+ }
305+ }
306+
317307#pragma mark - Core
318308
319309- (void )setImage : (UIImage *)image
@@ -449,7 +439,6 @@ - (void)_setDownloadIdentifier:(id)downloadIdentifier
449439 _downloadIdentifier = downloadIdentifier;
450440}
451441
452-
453442#pragma mark - Image Loading Machinery
454443
455444- (void )_loadImageIdentifiers
@@ -493,19 +482,37 @@ - (UIImage *)_bestImmediatelyAvailableImageFromDataSource:(id *)imageIdentifierO
493482
494483#pragma mark -
495484
496- /* *
497- @note: This should be called without _downloadIdentifierLock held. We will lock
498- super to read our interface state and it's best to avoid acquiring both locks.
499- */
485+ - (void )_updatePriorityOnDownloaderIfNeededWithDefaultPriority : (ASImageDownloaderPriority)defaultPriority
486+ {
487+ ASAssertUnlocked (_downloadIdentifierLock);
488+
489+ if (_downloaderFlags.downloaderImplementsSetPriority ) {
490+ // Read our interface state before locking so that we don't lock super while holding our lock.
491+ ASInterfaceState interfaceState = self.interfaceState ;
492+ ASDN::MutexLocker l (_downloadIdentifierLock);
493+
494+ if (_downloadIdentifier != nil ) {
495+ ASImageDownloaderPriority priority = defaultPriority;
496+ if (ASActivateExperimentalFeature (ASExperimentalImageDownloaderPriority)) {
497+ priority = ASImageDownloaderPriorityWithInterfaceState (interfaceState);
498+ }
499+
500+ [_downloader setPriority: priority withDownloadIdentifier: _downloadIdentifier];
501+ }
502+ }
503+ }
504+
500505- (void )_updateProgressImageBlockOnDownloaderIfNeeded
501506{
507+ ASAssertUnlocked (_downloadIdentifierLock);
508+
502509 BOOL shouldRenderProgressImages = self.shouldRenderProgressImages ;
503510
504511 // Read our interface state before locking so that we don't lock super while holding our lock.
505512 ASInterfaceState interfaceState = self.interfaceState ;
506513 ASDN::MutexLocker l (_downloadIdentifierLock);
507514
508- if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil ) {
515+ if (!_downloaderFlags. downloaderImplementsSetProgress || _downloadIdentifier == nil ) {
509516 return ;
510517 }
511518
@@ -825,7 +832,7 @@ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL c
825832 [_delegate multiplexImageNode: self didStartDownloadOfImageWithIdentifier: imageIdentifier];
826833
827834 __weak __typeof__ (self) weakSelf = self;
828- void (^ downloadProgressBlock)(CGFloat) = nil ;
835+ ASImageDownloaderProgress downloadProgressBlock = NULL ;
829836 if (_delegateFlags.downloadProgress ) {
830837 downloadProgressBlock = ^(CGFloat progress) {
831838 __typeof__ (self) strongSelf = weakSelf;
@@ -835,30 +842,67 @@ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL c
835842 };
836843 }
837844
845+ ASImageDownloaderCompletion completion = ^(id <ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
846+ // We dereference iVars directly, so we can't have weakSelf going nil on us.
847+ __typeof__ (self) strongSelf = weakSelf;
848+ if (!strongSelf)
849+ return ;
850+
851+ ASDN::MutexLocker l (strongSelf->_downloadIdentifierLock );
852+ // Getting a result back for a different download identifier, download must not have been successfully canceled
853+ if (ASObjectIsEqual (strongSelf->_downloadIdentifier , downloadIdentifier) == NO && downloadIdentifier != nil ) {
854+ return ;
855+ }
856+
857+ completionBlock ([imageContainer asdk_image ], error);
858+
859+ // Delegateify.
860+ if (strongSelf->_delegateFlags .downloadFinish )
861+ [strongSelf->_delegate multiplexImageNode: weakSelf didFinishDownloadingImageWithIdentifier: imageIdentifier error: error];
862+ };
863+
838864 // Download!
839865 ASPerformBlockOnBackgroundThread (^{
840- [self _setDownloadIdentifier: [_downloader downloadImageWithURL: imageURL
841- callbackQueue: dispatch_get_main_queue ()
842- downloadProgress: downloadProgressBlock
843- completion: ^(id <ASImageContainerProtocol> imageContainer, NSError *error, id downloadIdentifier, id userInfo) {
844- // We dereference iVars directly, so we can't have weakSelf going nil on us.
845- __typeof__ (self) strongSelf = weakSelf;
846- if (!strongSelf)
847- return ;
848-
849- ASDN::MutexLocker l (_downloadIdentifierLock);
850- // Getting a result back for a different download identifier, download must not have been successfully canceled
851- if (ASObjectIsEqual (_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil ) {
852- return ;
853- }
854-
855- completionBlock ([imageContainer asdk_image ], error);
856-
857- // Delegateify.
858- if (strongSelf->_delegateFlags .downloadFinish )
859- [strongSelf->_delegate multiplexImageNode: weakSelf didFinishDownloadingImageWithIdentifier: imageIdentifier error: error];
860- }]];
861- [self _updateProgressImageBlockOnDownloaderIfNeeded ];
866+ __typeof__ (self) strongSelf = weakSelf;
867+ if (!strongSelf)
868+ return ;
869+
870+ dispatch_queue_t callbackQueue = dispatch_get_main_queue ();
871+
872+ id downloadIdentifier;
873+ if (strongSelf->_downloaderFlags .downloaderImplementsDownloadWithPriority
874+ && ASActivateExperimentalFeature (ASExperimentalImageDownloaderPriority)) {
875+
876+ /*
877+ Decide a priority based on the current interface state of this node.
878+ It can happen that this method was called when the node entered preload state
879+ but the interface state, at this point, tells us that the node is (going to be) visible,
880+ If that's the case, we jump to a higher priority directly.
881+ */
882+ ASImageDownloaderPriority priority = ASImageDownloaderPriorityWithInterfaceState (strongSelf.interfaceState );
883+ downloadIdentifier = [strongSelf->_downloader downloadImageWithURL: imageURL
884+ priority: priority
885+ callbackQueue: callbackQueue
886+ downloadProgress: downloadProgressBlock
887+ completion: completion];
888+ } else {
889+ /*
890+ Kick off a download with default priority.
891+ The actual "default" value is decided by the downloader.
892+ ASBasicImageDownloader and ASPINRemoteImageDownloader both use ASImageDownloaderPriorityImminent
893+ which is mapped to NSURLSessionTaskPriorityDefault.
894+
895+ This means that preload and display nodes use the same priority
896+ and their requests are put into the same pool.
897+ */
898+ downloadIdentifier = [strongSelf->_downloader downloadImageWithURL: imageURL
899+ callbackQueue: callbackQueue
900+ downloadProgress: downloadProgressBlock
901+ completion: completion];
902+ }
903+
904+ [strongSelf _setDownloadIdentifier: downloadIdentifier];
905+ [strongSelf _updateProgressImageBlockOnDownloaderIfNeeded ];
862906 });
863907}
864908
0 commit comments