【问题标题】:Swift iOS -How to continue running a DispatchSemaphore combined with DispatchGroup()Swift iOS - 如何结合 DispatchGroup() 继续运行 DispatchSemaphore
【发布时间】:2019-04-28 21:43:25
【问题描述】:

vc1 推送 vc2 和 vc2。

在 vc2 中,我有一个字符串数组,我将其转换为最终通过 URLSession 的 url,并且返回的图像被转换为​​带有过滤器的图像。在 URLSession 回调中获得过滤后的图像后,我将其保存到缓存中。

我希望返回的过滤图像以字符串数组中的确切顺序显示。我关注this answer 并使用 DispatchGroup + Semaphores 并且一切正常,过滤图像的顺序以确切的顺序返回。

我遇到的问题是,如果用户返回 vc1 然后推送 vc2,一旦下面的代码运行并看到缓存有第一个过滤图像(“str1”),它会将其添加到过滤数组并跳转到dispatchGroup.notify。与数组中的“str1”关联的过滤图像是唯一附加的内容,而来自“str2”和“str3”的图像则不是。

缓存可能会或可能不会清除与“str2”、“str3”关联的过滤图像,如果它确实清除了它们,那么循环应该继续到 URLSession 回调,但它没有。但如果它没有清除它们,它也不会添加它们。 这就是我的问题发生的地方,一旦缓存被命中,循环就会停止运行。这是我第一次使用信号量,所以我不确定问题出在哪里。

我为解决这个问题所做的只是删除了缓存,一切正常,但这也意味着我没有得到使用缓存的好处。

let imageCache = NSCache<AnyObject, AnyObject>()

let arrOfStringsAsUrls = ["str1", "str2", "str3", "maybe more strings..."]

var filteredArray = [UIImage]()

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "any-label-name")
let dispatchSemaphore = DispatchSemaphore(value: 0)

dispatchQueue.async {

    for str in arrOfStringsAsUrls {

        dispatchGroup.enter()

        guard let url = URL(string: str) else {

            dispatchSemaphore.signal()
            dispatchGroup.leave()
            return
        }

        if let cachedImage = imageCache.object(forKey: str as AnyObject) as? UIImage {

            self?.filteredArray.append(cachedImage)

            dispatchSemaphore.signal()
            dispatchGroup.leave()
            return
        }

        URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

             if let error = error {       
                 dispatchSemaphore.signal()
                 dispatchGroup.leave()
                 return
             }

             guard let data = data, let image = UIImage(data: data) else {
                 dispatchSemaphore.signal()
                 dispatchGroup.leave()
                 return
             }

             DispatchQueue.main.async{ [weak self] in

                 let filteredImage = self?.addFilterToImage(image)

                 self?.imageCache.setObject(image, forKey: str as AnyObject)

                 self?.filteredArray.append(filteredImage)

                 dispatchSemaphore.signal()
                 dispatchGroup.leave()
             }
       }).resume()

       dispatchSemaphore.wait()
    }

    dispatchGroup.notify(queue: dispatchQueue) { in
        // reloadData() and do some other stuff on the main queue
    }
}

【问题讨论】:

    标签: ios swift for-loop grand-central-dispatch semaphore


    【解决方案1】:

    在第二个 VC 的第一次推送期间,所有图像都没有在缓存中,所以没有警卫 else 触发,还有if let cachedImage = imageCache...,所以一切顺利,在下一次推送之后,缓存存储了图像,所以 this if let 触发

    if let cachedImage = imageCache.object(forKey: str as AnyObject) as? UIImage {
      ....
      return
    }
    

    并且由于您放置了 return 语句,它将导致 for 循环和函数的退出,导致随后的图像捕获结束,从而触发调度组通知,因为 enter 的计数相等/ leave 在那一刻,您真正需要的是当缓存中存储的图像或 url 错误时停止使用会话获取它,并且在 for 循环中完成这项工作的最佳方法是 continue 关键字,它将跳过当前迭代并跳转到下一个迭代

    guard let url = URL(string: str) else { 
       dispatchSemaphore.signal()
       dispatchGroup.leave()
       continue
    }
    
    if let cachedImage = imageCache.object(forKey: str as AnyObject) as? UIImage { 
        self?.filteredArray.append(cachedImage) 
        dispatchSemaphore.signal()
        dispatchGroup.leave()
        continue
    }
    

    【讨论】:

    • 抱歉,我刚刚注意到您更改了所有内容以继续。但是,是的,我最初认为这是返回,但后来我意识到所有的守卫语句都返回了。我会在大约 20 分钟后回到我的电脑前继续尝试。问题,继续不意味着继续……那不会因为出现问题而破坏停止的目的。例如,在第一个保护语句中,如果 url 格式不正确,为什么还要继续?
    • 我最初使用嵌套的 if let(在添加信号量和缓存之前),但它是一个丑陋的代码,在添加信号量时变得更糟,所以我选择了更清洁的守卫。看起来也许这是唯一的方法???
    • continue inside for 循环意味着跳过当前迭代并跳转到下一个迭代,当您需要终止不在内部的任何即将到来的操作时,您使用 return inside guard's else一个 for 循环,if-let 是另一种方式,但你不必守卫会做这项工作
    • 好的,谢谢。我实际上从未见过有人使用 continue 和警卫声明。每天学些新东西! ?。过几次我会试试的。看起来它应该可以工作
    • 我也从未使用过 continue。我从来不需要它
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2018-11-01
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多