PHCachingImageManager 有时不返回结果或错误

问题描述 投票:0回答:1

当用户上下滚动显示这些图像的网格视图时,我使用

PHCachingImageManager
请求一系列图像的缩略图。我正在使用如下
async
函数来支持具有
async-await
风格的现代快速并发。

class PhotoRequestManager {
  // Properties from class
  private var imageCachingManager = PHCachingImageManager()

  func fetchImage(
    asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode
  ) async throws -> UIImage? {
    
    let options = PHImageRequestOptions()
    options.deliveryMode = .opportunistic
    options.resizeMode = .fast
    options.isNetworkAccessAllowed = true
    options.isSynchronous = true

    return try await withCheckedThrowingContinuation { [weak self] continuation in
      self?.imageCachingManager.requestImage(
        for: asset, targetSize: targetSize, contentMode: contentMode, options: options
      ) { image, info in
        /// When the hang occurs, this block never gets executed.
        if let error = info?[PHImageErrorKey] as? Error {
          continuation.resume(throwing: error)
          return
        }
        continuation.resume(returning: image)
      }
    }
  }

}

// From UI
struct MySwiftUIView: View {
  @EnvironmentObject private var photosRequestManager: PhotoRequestManager

  var body: some View {
    ZStack {
      // Content
    }
    .task { await requestImage() }
  }

  @State private var thumbnail: Image?

  private func requestImage() async {
    guard
      let uiImage = try? await photosRequestManager.fetchImage(
        asset: asset, targetSize: size)
    else {
      return
    }

    thumbnail = Image(uiImage: uiImage)
  }
}

我遇到了一个奇怪的问题,有时这个函数会挂起而不返回结果或错误。没有控制台错误或任何可见的内容,只是该函数从不返回值。发生这种情况时,对同一函数的任何后续请求也会挂起。如果有人知道这里可能发生什么,非常感谢任何帮助。

swift objective-c swiftui async-await phimagemanager
1个回答
0
投票

我不熟悉不调用

requestImage
完成处理程序的场景。 (如果有的话,您实际上遇到了相反的问题,即使用
.opportunistic
可能会导致完成处理程序被多次调用,这对于您的简单延续模式来说将是一个问题。您确实应该使用
AsyncStream
 AsyncThrowingStream
如果处理
.opportunistic
交付模式。请参阅此答案后半部分的示例。)

您应该确认 (a)

requestImage
正在接到电话; (b) 其完成处理程序也被调用; (c) 确定问题是否确实是未调用完成处理程序闭包,或者问题是否是
?
之一隐藏了某些问题。

有几个考虑因素:

  1. try?

    考虑:

    guard let uiImage = try? await photosRequestManager.fetchImage(
        asset: asset,
        targetSize: size
    ) else {
        return
    }
    

    如果抛出错误,

    try?
    将丢弃错误(及其有意义的诊断信息),默默退出并且永远不会满足继续。我建议:

    • try?
      替换为
      do
      -
      try
      -
      catch
    • 如果发现错误,将错误记录到控制台并通过
      continuation.resume(throwing: …)
      ;
    • 报告错误
    • 即使没有抛出错误,也不太可能,但您必须提前退出,因为图像是
      nil
      ,记录该事实,并使用您自己的自定义错误继续恢复。
  2. self?

    考虑:

    func fetchImage(
        asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode
    ) async throws -> UIImage? {
        let options = …
    
        return try await withCheckedThrowingContinuation { [weak self] continuation in
            self?.imageCachingManager.requestImage(…) { image, info in
                …
            }
        }
    }
    

    在不太可能的情况下,

    self
    nil
    requestImage
    永远不会被调用,并且延续永远不会被满足。就个人而言,我建议不要在延续中使用
    [weak self]
    捕获列表。

简而言之,我建议查看所有执行路径,并确保没有任何路径可以阻止继续得到满足。但现在,如果出现任何错误或

self
超出范围,您将默默地失败,并且延续将保持未解决状态。

© www.soinside.com 2019 - 2024. All rights reserved.