Merge pull request #1526 from Adlai-Holler/ImageNodeImprovements

[ASImageNode] Improvements to progressive decoding and cache accesses.
This commit is contained in:
appleguy
2016-04-23 23:05:41 -07:00
2 changed files with 102 additions and 86 deletions

View File

@@ -22,6 +22,7 @@
#import "ASPhotosFrameworkImageRequest.h"
#import "ASEqualityHelpers.h"
#import "ASInternalHelpers.h"
#import "ASDisplayNodeExtras.h"
#if !AS_IOS8_SDK_OR_LATER
#error ASMultiplexImageNode can be used on iOS 7, but must be linked against the iOS 8 SDK.
@@ -305,32 +306,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}
}
if (_downloaderImplementsSetProgress) {
ASDN::MutexLocker l(_downloadIdentifierLock);
if (_downloadIdentifier != nil) {
__weak __typeof__(self) weakSelf = self;
ASImageDownloaderProgressImage progress = nil;
if (isVisible) {
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
strongSelf.image = progressImage;
};
}
[_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
}
}
[self _updateProgressImageBlockOnDownloaderIfNeeded];
}
#pragma mark - Core
@@ -441,9 +417,6 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
- (void)_loadImageIdentifiers
{
// Kill any in-flight downloads.
[self _setDownloadIdentifier:nil];
// Grab the best possible image we can load right now.
id bestImmediatelyAvailableImageIdentifier = nil;
UIImage *bestImmediatelyAvailableImage = [self _bestImmediatelyAvailableImageFromDataSource:&bestImmediatelyAvailableImageIdentifier];
@@ -478,6 +451,41 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}
#pragma mark -
/**
@note: This should be called without _downloadIdentifierLock held. We will lock
super to read our interface state and it's best to avoid acquiring both locks.
*/
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
{
// Read our interface state before locking so that we don't lock super while holding our lock.
ASInterfaceState interfaceState = self.interfaceState;
ASDN::MutexLocker l(_downloadIdentifierLock);
if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil) {
return;
}
ASImageDownloaderProgressImage progress = nil;
if (ASInterfaceStateIncludesVisible(interfaceState)) {
__weak __typeof__(self) weakSelf = self;
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
ASDN::MutexLocker l(strongSelf->_downloadIdentifierLock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
strongSelf.image = progressImage;
};
}
[_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
}
- (void)_clearImage
{
// Destruction of bigger images on the main thread can be expensive
@@ -823,6 +831,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}]];
#pragma clang diagnostic pop
}
[self _updateProgressImageBlockOnDownloaderIfNeeded];
});
}

View File

@@ -15,6 +15,7 @@
#import "ASThread.h"
#import "ASInternalHelpers.h"
#import "ASImageContainerProtocolCategories.h"
#import "ASDisplayNodeExtras.h"
#if PIN_REMOTE_IMAGE
#import "ASPINRemoteImageDownloader.h"
@@ -214,7 +215,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
- (void)visibilityDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
if (_downloaderImplementsSetPriority) {
ASDN::MutexLocker l(_lock);
if (_downloadIdentifier != nil) {
@@ -225,32 +226,8 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
}
}
}
if (_downloaderImplementsSetProgress) {
ASDN::MutexLocker l(_lock);
if (_downloadIdentifier != nil) {
__weak __typeof__(self) weakSelf = self;
ASImageDownloaderProgressImage progress = nil;
if (isVisible) {
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
ASDN::MutexLocker l(_lock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
strongSelf.image = progressImage;
};
}
[_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
}
}
[self _updateProgressImageBlockOnDownloaderIfNeeded];
}
- (void)clearFetchedData
@@ -280,6 +257,40 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
#pragma mark - Private methods -- only call with lock.
/**
@note: This should be called without _lock held. We will lock
super to read our interface state and it's best to avoid acquiring both locks.
*/
- (void)_updateProgressImageBlockOnDownloaderIfNeeded
{
// Read our interface state before locking so that we don't lock super while holding our lock.
ASInterfaceState interfaceState = self.interfaceState;
ASDN::MutexLocker l(_lock);
if (!_downloaderImplementsSetProgress || _downloadIdentifier == nil) {
return;
}
ASImageDownloaderProgressImage progress = nil;
if (ASInterfaceStateIncludesVisible(interfaceState)) {
__weak __typeof__(self) weakSelf = self;
progress = ^(UIImage * _Nonnull progressImage, id _Nullable downloadIdentifier) {
__typeof__(self) strongSelf = weakSelf;
if (strongSelf == nil) {
return;
}
ASDN::MutexLocker l(strongSelf->_lock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
strongSelf.image = progressImage;
};
}
[_downloader setProgressImageBlock:progress callbackQueue:dispatch_get_main_queue() withDownloadIdentifier:_downloadIdentifier];
}
- (void)_clearImage
{
// Destruction of bigger images on the main thread can be expensive
@@ -339,6 +350,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
}];
#pragma clang diagnostic pop
}
[self _updateProgressImageBlockOnDownloaderIfNeeded];
});
}
@@ -386,37 +398,32 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
return;
}
{
ASDN::MutexLocker l(strongSelf->_lock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
if (imageContainer != nil) {
strongSelf->_imageLoaded = YES;
if ([imageContainer asdk_animatedImageData] && _downloaderImplementsAnimatedImage) {
strongSelf.animatedImage = [_downloader animatedImageWithData:[imageContainer asdk_animatedImageData]];
} else {
strongSelf.image = [imageContainer asdk_image];
}
}
strongSelf->_downloadIdentifier = nil;
strongSelf->_cacheUUID = nil;
ASDN::MutexLocker l(strongSelf->_lock);
//Getting a result back for a different download identifier, download must not have been successfully canceled
if (ASObjectIsEqual(strongSelf->_downloadIdentifier, downloadIdentifier) == NO && downloadIdentifier != nil) {
return;
}
{
ASDN::MutexLocker l(strongSelf->_lock);
if (imageContainer != nil) {
[strongSelf->_delegate imageNode:strongSelf didLoadImage:strongSelf.image];
}
else if (error && _delegateSupportsDidFailWithError) {
[strongSelf->_delegate imageNode:strongSelf didFailWithError:error];
if (imageContainer != nil) {
strongSelf->_imageLoaded = YES;
if ([imageContainer asdk_animatedImageData] && _downloaderImplementsAnimatedImage) {
strongSelf.animatedImage = [_downloader animatedImageWithData:[imageContainer asdk_animatedImageData]];
} else {
strongSelf.image = [imageContainer asdk_image];
}
}
strongSelf->_downloadIdentifier = nil;
strongSelf->_cacheUUID = nil;
if (imageContainer != nil) {
[strongSelf->_delegate imageNode:strongSelf didLoadImage:strongSelf.image];
}
else if (error && strongSelf->_delegateSupportsDidFailWithError) {
[strongSelf->_delegate imageNode:strongSelf didFailWithError:error];
}
};
if (_cache != nil) {
@@ -425,14 +432,14 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
void (^cacheCompletion)(id <ASImageContainerProtocol>) = ^(id <ASImageContainerProtocol> imageContainer) {
// If the cache UUID changed, that means this request was cancelled.
if (![_cacheUUID isEqual:cacheUUID]) {
if (!ASObjectIsEqual(_cacheUUID, cacheUUID)) {
return;
}
if ([imageContainer asdk_image] == NULL && _downloader != nil) {
if ([imageContainer asdk_image] == nil && _downloader != nil) {
[self _downloadImageWithCompletion:finished];
} else {
finished(imageContainer, NULL, nil);
finished(imageContainer, nil, nil);
}
};