如何使用 DispatchGroup 查找照片并存储在 UIImage 数组中

Posted

技术标签:

【中文标题】如何使用 DispatchGroup 查找照片并存储在 UIImage 数组中【英文标题】:How can I use DispatchGroup to look up photos and store in UIImage array 【发布时间】:2018-09-05 03:24:35 【问题描述】:

我查看了很多使用调度组的示例,但我似乎无法让它发挥作用。

我在我的项目中使用 Google 地图,并在附近的商家处放置了标记。单击标记时,我想显示另一个具有该地点图像的视图控制器。

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool 
  self.loadPhotosForSalonByPlaceId(placeID: poiItem.placeId)
  photoDispatchGroup.wait()
  let salonInfoViewController = self.addPullUpController() //show view controller


  salonInfoViewController.ImageCarouselView.images = self.salonImages
  salonInfoViewController.salonName.text = poiItem.name
  salonInfoViewController.salonAddress.text = poiItem.address
  salonInfoViewController.openAppointmentSlotArray = self.openAppointmentSlots

  self.isSalonInfoViewPresented = true
  return isSalonInfoViewPresented

这就是我的 loadPhotosForSalonByPlaceId 的样子:

func loadPhotosForSalonByPlaceId(placeID: String)

    var countToLimitToFiveImages = 0
    GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID)  (photos, error) -> Void in

        if let error = error 
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
         else 
            for photoMetadata in (photos?.results)! 
                if countToLimitToFiveImages == 5 
                    break;
                
                self.photoDispatchGroup.enter()
                self.loadImageForMetadata(photoMetadata: photoMetadata)
                self.photoDispatchGroup.leave()
                countToLimitToFiveImages += 1
            
        
    

我是否错误地使用了输入和离开?还是我应该在lookUpPhotos 完成后通知主线程继续?因为现在,当我想显示视图控制器时,UIImages 数组是空的。

提前致谢!

下面的代码是我在 loadPhotosForSalonByPlaceId 函数中调用的代码。它将 PhotoMetaData 转换为 UIImage,我将其附加到我的数组中。据我了解,查找照片和 loadplacephoto 是异步调用。完成这两项任务后,如何使用 DispatchGroup 显示我的视图控制器。

func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata) 
    var image = UIImage()
    GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: 
        (photo, error) -> Void in
        if let error = error 
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")

         else 
            image = photo!
            self.salonImages.append(image)

        
    )

【问题讨论】:

【参考方案1】:

您目前对 DispatchGroup 的使用完全错误。您应该在异步进程开始之前调用enter,并在异步进程完成时调用leave。并且永远不要在主队列上调用wait。你会想使用notify。不过,在这种情况下不要使用调度组。

这里有两个级别的异步调用。第一个在loadImageForMetadata。这需要重构以提供完成处理程序。第二个在loadPhotosForSalonByPlaceId 中,也需要重构以提供完成处理程序。您还需要重做您使用调度组的方式。

这是你重构的loadImageForMetadata

func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata, completion: (Bool) -> Void) 
    var image = UIImage()
    GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: 
        (photo, error) -> Void in
        if let error = error 
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
            completion(false)
         else 
            image = photo!
            self.salonImages.append(image)
            completion(true)
        
    )

这里是重构的loadPhotosForSalonByPlaceId,带有完成处理程序和正确使用 DispatchGroup:

func loadPhotosForSalonByPlaceId(placeID: String, completion: (Bool) -> Void) 
    var countToLimitToFiveImages = 0
    GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID)  (photos, error) -> Void in
        if let error = error 
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
            completion(false)
         else 
            let group = DispatchGroup()

            for photoMetadata in (photos?.results)! 
                if countToLimitToFiveImages == 5 
                    break;
                
                group.enter()
                self.loadImageForMetadata(photoMetadata: photoMetadata)  (success) in
                     if success 
                         countToLimitToFiveImages += 1
                     
                     group.leave()
                
            

            group.notify(queue: DispatchQueue.global()) 
                completion(true)
            
        
    

然后更新你的用法:

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool 
    self.loadPhotosForSalonByPlaceId(placeID: poiItem.placeId)  (success) in
        if success 
            DispatchQueue.main.async 
                let salonInfoViewController = self.addPullUpController() //show view controller

                salonInfoViewController.ImageCarouselView.images = self.salonImages
                salonInfoViewController.salonName.text = poiItem.name
                salonInfoViewController.salonAddress.text = poiItem.address
                salonInfoViewController.openAppointmentSlotArray = self.openAppointmentSlots
            
        
    

    self.isSalonInfoViewPresented = true
    return isSalonInfoViewPresented

【讨论】:

感谢您的精彩回答。但我有个问题。如果我想使用多个异步调用怎么办?那么 DispatchGroup 会派上用场吗? @SishirMohan 是的,调度组在处理多个异步调用时非常有用,并且您想在所有调用都完成后做一些事情。 感谢您的快速回复。我已经编辑了我的问题,您能看看我如何利用 Dispatch Group 进行两个异步调用吗? 我已根据您关于loadImageForMetadata 的最新信息重写了我的答案。

以上是关于如何使用 DispatchGroup 查找照片并存储在 UIImage 数组中的主要内容,如果未能解决你的问题,请参考以下文章

等待循环完成,DispatchGroup 使用 Swift 从 firebase 获取数据

如何查找 iPhone 相机照片的大小以及如何调整大小?

Swift 伪原子并发同步代码引起 DispatchGroup.leave() 方法不平衡调用导致 App 崩溃的解决

Swift 伪原子并发同步代码引起 DispatchGroup.leave() 方法不平衡调用导致 App 崩溃的解决

使用 DispatchGroup、DispatchQueue 和 DispatchSemaphore 按顺序执行带有 for 循环的 Swift 4 异步调用

如何在 IOS 中查找照片资产的标识符