3

I have a collection view where I display all user's photos. Basic stuff, a Data Source who fetches PHAssets and use requestImage on a PHCachingImageManager to load thumbnails.

But recently I got a bug report where the UI freezes when you have more then 5000 images and quickly scrolls through it. Upon investigating, I was able to reproduce the issue, and it seems that the main thread is locked (_lock_wait) right after calling requestImage, while dozens of other threads created by that call (the thumbnails for the other cells) are also locked waiting for who knows what.

I tried several things and nothing works:

  1. Implemented UICollectionViewDataSourcePrefetching, and used PHCachingImageManager to startCachingImages on the prefetchItemsAt event. Funny thing, it made things even worse, as it tries to load more images at once. And the cancelPrefetchingForItemsAt, who was supposed to be called when the cell is out of screen, is never called.

  2. Tried to call cancelImageRequest on slower requests, also no success here.

Now I don't know what else to do, I could run requestImage on a background dispatch_queue so it won't lock my main thread, but it feels weird since the method will spawn another thread by itself

My code is something like that:

let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .fast
requestOptions.deliveryMode = .opportunistic
requestOptions.isSynchronous = false
requestOptions.isNetworkAccessAllowed = true
return requestOptions

myManager.requestImage(for: asset, targetSize: CGSize(width: 375, height: 375), contentMode: .aspectFill, options: options) { /* use image */ }

ps: I'm not sure, but it seems this only happen on iOS 11, right now I only was able to reproduce on an iPhone X

Paulo Cesar
  • 2,250
  • 1
  • 25
  • 35
  • Didd you check with synchrnous mode to true – karthikeyan Aug 16 '18 at 12:54
  • I'm testing it right now, it works better, but then I lose the option of loading a faster, lower quality image before the high quality one is done... – Paulo Cesar Aug 16 '18 at 13:44
  • Oh, I still get the lock, but now in a later stage of the app when I set the current opengl context. This is really weird. – Paulo Cesar Aug 16 '18 at 13:49
  • It seems that cancelImageRequest when reusing the cells did the job! But I still need to confirm with the client. I'll update the answer if this works – Paulo Cesar Aug 16 '18 at 14:03

1 Answers1

2

It turns out this was the issue: GCD dispatch concurrent queue freeze with 'Dispatch Thread Soft Limit Reached: 64' in crash log

requestImage keep creating new threads until it reaches the dispatch thread limit and UI freezes on this _lock_wait

Solution was to set the requestOptions.isSynchronous = true, create an OperationQueue with limited concurrency and add the image fetching as jobs on that queue.

//somewhere like viewDidLoad
operationQueue.maxConcurrentOperationCount = 54

//when setting up the cells
operationQueue.addOperation { [weak self] in
      self?.cachingImageManager.requestImage(/* params...*/) { (image) in
            OperationQueue.main.addOperation {
               //use image
            }
      }
}
Paulo Cesar
  • 2,250
  • 1
  • 25
  • 35
  • 1
    How do you get lower-quality images on fast scrolling in this case? Also, how do you manage request cancellation? – demon9733 May 13 '21 at 12:33