Limit 1 inflight Photos.framework request per multiplex image node

This commit is contained in:
Adlai Holler
2015-10-05 23:01:03 -07:00
parent d94dcdef0f
commit f0b7e150cd

View File

@@ -66,6 +66,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
id _loadedImageIdentifier; id _loadedImageIdentifier;
id _loadingImageIdentifier; id _loadingImageIdentifier;
id _displayedImageIdentifier; id _displayedImageIdentifier;
NSOperation *_phImageRequestOperation;
// Networking. // Networking.
id _downloadIdentifier; id _downloadIdentifier;
@@ -168,12 +169,21 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
return [self initWithCache:nil downloader:nil]; // satisfy compiler return [self initWithCache:nil downloader:nil]; // satisfy compiler
} }
- (void)dealloc {
[_phImageRequestOperation cancel];
}
#pragma mark - ASDisplayNode Overrides #pragma mark - ASDisplayNode Overrides
- (void)clearContents - (void)clearContents
{ {
[super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful. [super clearContents]; // This actually clears the contents, so we need to do this first for our displayedImageIdentifier to be meaningful.
[self _setDisplayedImageIdentifier:nil withImage:nil]; [self _setDisplayedImageIdentifier:nil withImage:nil];
if (_phImageRequestOperation) {
[_phImageRequestOperation cancel];
_phImageRequestOperation = nil;
}
if (_downloadIdentifier) { if (_downloadIdentifier) {
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier]; [_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
_downloadIdentifier = nil; _downloadIdentifier = nil;
@@ -521,22 +531,33 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
/* /*
* Locking rationale: * Locking rationale:
* As of iOS 9, Photos.framework will eventually deadlock if you hit it with concurrent fetch requests. rdar://22984886 * As of iOS 9, Photos.framework will eventually deadlock if you hit it with concurrent fetch requests. rdar://22984886
* Image requests are OK, but metadata requests aren't, so we limit ourselves to one at a time. * Concurrent image requests are OK, but metadata requests aren't, so we limit ourselves to one at a time.
* -[PHFetchResult dealloc] plays a role in this deadlock, so we help the fetch result die ASAP by never storing it.
*/ */
static NSLock *phRequestLock; static NSLock *phRequestLock;
static NSOperationQueue *phImageRequestQueue;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
phRequestLock = [NSLock new]; phRequestLock = [NSLock new];
phImageRequestQueue = [NSOperationQueue new];
phImageRequestQueue.maxConcurrentOperationCount = 10;
phImageRequestQueue.name = @"org.AsyncDisplayKit.MultiplexImageNode.phImageRequestQueue";
}); });
// This is sometimes called on main but there's no reason to stay there // Each ASMultiplexImageNode can have max 1 inflight Photos image request operation
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ [_phImageRequestOperation cancel];
__weak __typeof(self)weakSelf = self;
_phImageRequestOperation = [NSBlockOperation blockOperationWithBlock:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf == nil) { return; }
// Get the PHAsset itself. // Get the PHAsset itself.
[phRequestLock lock]; [phRequestLock lock];
PHAsset *imageAsset = [PHAsset fetchAssetsWithLocalIdentifiers:@[request.assetIdentifier] options:nil].firstObject; PHAsset *imageAsset;
// -[PHFetchResult dealloc] plays a role in the deadlock mentioned above, so we make sure the PHFetchResult is deallocated inside the critical section
@autoreleasepool {
imageAsset = [PHAsset fetchAssetsWithLocalIdentifiers:@[request.assetIdentifier] options:nil].firstObject;
}
[phRequestLock unlock]; [phRequestLock unlock];
if (imageAsset == nil) { if (imageAsset == nil) {
@@ -558,7 +579,7 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
options.synchronous = YES; options.synchronous = YES;
} }
PHImageManager *imageManager = self.imageManager ?: PHImageManager.defaultManager; PHImageManager *imageManager = strongSelf.imageManager ?: PHImageManager.defaultManager;
[imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) { [imageManager requestImageForAsset:imageAsset targetSize:request.targetSize contentMode:request.contentMode options:options resultHandler:^(UIImage *image, NSDictionary *info) {
if (NSThread.isMainThread) { if (NSThread.isMainThread) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
@@ -568,7 +589,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
completionBlock(image, info[PHImageErrorKey]); completionBlock(image, info[PHImageErrorKey]);
} }
}]; }];
}); }];
[phImageRequestQueue addOperation:_phImageRequestOperation];
} }
- (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock - (void)_fetchImageWithIdentifierFromCache:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image))completionBlock