2525
2626@interface ASNetworkImageNode ()
2727{
28- __weak id <ASImageCacheProtocol> _cache;
29- __weak id <ASImageDownloaderProtocol> _downloader;
30-
3128 // Only access any of these with __instanceLock__.
3229 __weak id <ASNetworkImageNodeDelegate> _delegate;
3330
@@ -53,6 +50,9 @@ @interface ASNetworkImageNode ()
5350 } _delegateFlags;
5451
5552 // set on init only
53+ __weak id <ASImageDownloaderProtocol> _downloader;
54+ __weak id <ASImageCacheProtocol> _cache;
55+
5656 struct {
5757 unsigned int downloaderImplementsSetProgress:1 ;
5858 unsigned int downloaderImplementsSetPriority:1 ;
@@ -236,11 +236,13 @@ - (void)setDelegate:(id<ASNetworkImageNodeDelegate>)delegate
236236
237237- (void )setShouldRenderProgressImages : (BOOL )shouldRenderProgressImages
238238{
239- ASDN::MutexLocker l (__instanceLock__);
240- if (shouldRenderProgressImages == _shouldRenderProgressImages) {
241- return ;
239+ {
240+ ASDN::MutexLocker l (__instanceLock__);
241+ if (shouldRenderProgressImages == _shouldRenderProgressImages) {
242+ return ;
243+ }
244+ _shouldRenderProgressImages = shouldRenderProgressImages;
242245 }
243- _shouldRenderProgressImages = shouldRenderProgressImages;
244246
245247 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
246248}
@@ -293,28 +295,41 @@ - (void)displayWillStartAsynchronously:(BOOL)asynchronously
293295- (void )didEnterVisibleState
294296{
295297 [super didEnterVisibleState ];
296- ASDN::MutexLocker l (__instanceLock__);
298+
299+ id downloadIdentifier = nil ;
300+
301+ {
302+ ASDN::MutexLocker l (__instanceLock__);
297303
298- if (_downloaderFlags.downloaderImplementsSetPriority ) {
299- if (_downloadIdentifier != nil ) {
300- [_downloader setPriority: ASImageDownloaderPriorityVisible withDownloadIdentifier: _downloadIdentifier];
304+ if (_downloaderFlags.downloaderImplementsSetPriority ) {
305+ downloadIdentifier = _downloadIdentifier;
301306 }
302307 }
303308
309+ if (downloadIdentifier != nil ) {
310+ [_downloader setPriority: ASImageDownloaderPriorityVisible withDownloadIdentifier: downloadIdentifier];
311+ }
312+
304313 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
305314}
306315
307316- (void )didExitVisibleState
308317{
309318 [super didExitVisibleState ];
310- ASDN::MutexLocker l (__instanceLock__);
319+
320+ id downloadIdentifier = nil ;
311321
312- if (_downloaderFlags.downloaderImplementsSetPriority ) {
313- if (_downloadIdentifier != nil ) {
314- [_downloader setPriority: ASImageDownloaderPriorityPreload withDownloadIdentifier: _downloadIdentifier];
322+ {
323+ ASDN::MutexLocker l (__instanceLock__);
324+ if (_downloaderFlags.downloaderImplementsSetPriority ) {
325+ downloadIdentifier = _downloadIdentifier;
315326 }
316327 }
317328
329+ if (downloadIdentifier != nil ) {
330+ [_downloader setPriority: ASImageDownloaderPriorityPreload withDownloadIdentifier: downloadIdentifier];
331+ }
332+
318333 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
319334}
320335
@@ -370,9 +385,16 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded
370385 }
371386
372387 // Read state.
373- BOOL shouldRender = _shouldRenderProgressImages && ASInterfaceStateIncludesVisible (_interfaceState);
374- id oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock;
375- id newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil ;
388+ BOOL shouldRender;
389+ id oldDownloadIDForProgressBlock;
390+ id newDownloadIDForProgressBlock;
391+ BOOL clearAndReattempt = NO ;
392+ {
393+ ASDN::MutexLocker l (__instanceLock__);
394+ shouldRender = _shouldRenderProgressImages && ASInterfaceStateIncludesVisible (_interfaceState);
395+ oldDownloadIDForProgressBlock = _downloadIdentifierForProgressBlock;
396+ newDownloadIDForProgressBlock = shouldRender ? _downloadIdentifier : nil ;
397+ }
376398
377399 // If we're already bound to the correct download, we're done.
378400 if (ASObjectIsEqual (oldDownloadIDForProgressBlock, newDownloadIDForProgressBlock)) {
@@ -393,7 +415,19 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded
393415 }
394416
395417 // Update state.
396- _downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock;
418+ {
419+ ASDN::MutexLocker l (__instanceLock__);
420+ if (_downloadIdentifierForProgressBlock == oldDownloadIDForProgressBlock) {
421+ _downloadIdentifierForProgressBlock = newDownloadIDForProgressBlock;
422+ } else {
423+ clearAndReattempt = YES ;
424+ }
425+ }
426+
427+ if (clearAndReattempt) {
428+ [_downloader setProgressImageBlock: nil callbackQueue: dispatch_get_main_queue () withDownloadIdentifier: newDownloadIDForProgressBlock];
429+ [self _updateProgressImageBlockOnDownloaderIfNeeded ];
430+ }
397431}
398432
399433- (void )_cancelDownloadAndClearImage
@@ -443,19 +477,46 @@ - (void)_clearImage
443477- (void )_downloadImageWithCompletion : (void (^)(id <ASImageContainerProtocol> imageContainer, NSError *, id downloadIdentifier))finished
444478{
445479 ASPerformBlockOnBackgroundThread (^{
480+ NSURL *url;
481+ id downloadIdentifier;
482+ BOOL cancelAndReattempt = NO ;
446483
447- ASDN::MutexLocker l (__instanceLock__);
448- _downloadIdentifier = [_downloader downloadImageWithURL: _URL
449- callbackQueue: dispatch_get_main_queue ()
450- downloadProgress: NULL
451- completion: ^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
452- if (finished != NULL ) {
453- finished (imageContainer, error, downloadIdentifier);
454- }
455- }];
484+ // Below, to avoid performance issues, we're calling downloadImageWithURL without holding the lock. This is a bit ugly because
485+ // We need to reobtain the lock after and ensure that the task we've kicked off still matches our URL. If not, we need to cancel
486+ // it and try again.
487+ {
488+ ASDN::MutexLocker l (__instanceLock__);
489+ url = _URL;
490+ }
491+
492+ downloadIdentifier = [_downloader downloadImageWithURL: url
493+ callbackQueue: dispatch_get_main_queue ()
494+ downloadProgress: NULL
495+ completion: ^(id <ASImageContainerProtocol> _Nullable imageContainer, NSError * _Nullable error, id _Nullable downloadIdentifier) {
496+ if (finished != NULL ) {
497+ finished (imageContainer, error, downloadIdentifier);
498+ }
499+ }];
456500
501+ {
502+ ASDN::MutexLocker l (__instanceLock__);
503+ if ([_URL isEqual: url]) {
504+ // The download we kicked off is correct, no need to do any more work.
505+ _downloadIdentifier = downloadIdentifier;
506+ } else {
507+ // The URL changed since we kicked off our download task. This shouldn't happen often so we'll pay the cost and
508+ // cancel that request and kick off a new one.
509+ cancelAndReattempt = YES ;
510+ }
511+ }
512+
513+ if (cancelAndReattempt) {
514+ [_downloader cancelImageDownloadForIdentifier: downloadIdentifier];
515+ [self _downloadImageWithCompletion: finished];
516+ return ;
517+ }
518+
457519 [self _updateProgressImageBlockOnDownloaderIfNeeded ];
458-
459520 });
460521}
461522
0 commit comments