简体   繁体   English

Swift iOS-如何继续运行与DispatchGroup()结合的DispatchSemaphore

[英]Swift iOS -How to continue running a DispatchSemaphore combined with DispatchGroup()

vc1 pushes on vc2 and in vc2. vc1推送vc2和vc2。

In vc2 I have an array of strings that I convert to urls which eventually go through a URLSession and the images returned get converted to images with filters. 在vc2中,我有一个字符串数组,我转换为url,最终通过URLSession,返回的图像转换为带过滤器的图像。 Once I get the filtered image in the URLSession callback I save it to cache. 一旦我在URLSession回调中获得过滤后的图像,我将其保存到缓存中。

I want the filtered images that are returned to be displayed in the exact order that is in the array of strings. 我希望返回的过滤图像以字符串数组中的确切顺序显示。 I followed this answer and use DispatchGroup + Semaphores and everything works fine, the order of the filtered images are returned in the exact order. 我按照这个答案并使用DispatchGroup + Semaphores,一切正常,过滤后的图像的顺序按照确切的顺序返回。

The problem I ran into is if the user returns back to vc1 then pushes on vc2, once the code below runs and sees that the cache has the first filtered image ("str1"), it adds it to the filteredArray and jumps to dispatchGroup.notify . 我遇到的问题是,如果用户返回vc1然后按下vc2,一旦下面的代码运行并看到缓存有第一个过滤的图像(“str1”),它就会将它添加到filteredArray并跳转到dispatchGroup.notify The filtered images associated with "str1" from the array is the only thing that is appended and the images from "str2" and "str3" aren't. 与数组中的“str1”相关联的滤波图像是唯一附加的图像,而来自“str2”和“str3”的图像不是。

The cache might or might not have purged the filtered images associated with "str2", "str3" and if it did purge them then the loop should continue to the URLSession callback but it doesn't. 缓存可能会或可能不会清除与“str2”,“str3”关联的已过滤图像,如果它确实清除了它们,那么循环应该继续到URLSession回调但它不会。 But if it didn't purge them it also doesn't add them. 但如果它没有清除它们,它也不会添加它们。 That is where my problem occurs, the loop stops running once the cache is hit. 这就是我的问题发生的地方,一旦达到缓存,循环就会停止运行。 This is my first time using a semaphore so I'm not sure where the issues is arising from. 这是我第一次使用信号量,所以我不确定问题出在哪里。

What I did to get around the problem was I simply removed the cache and everything works fine but that also means I don't get the benefit of using the cache. 我解决这个问题的方法是我只是删除缓存,一切正常,但这也意味着我没有得到使用缓存的好处。

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
    }
}

During the first push of the second VCs all images are not in cache so no guard else triggers and also if let cachedImage = imageCache... so everything goes smoothly , after the next push the cache has images stored so this if let triggers 在第一次推送第二个VC期间,所有图像都不在缓存中,因此没有其他防护触发器, if let cachedImage = imageCache...所以一切顺利,在下一次推送后缓存存储了图像,这样如果让触发器

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

and since you put a return statement , it will cause the exit of the for-loop and function , causing the subsequent catches of images to end which triggers the disptach group notify as there is an equal count of enter / leave at that moment , what you really need is when there is an image stored in cache or url is wrong to stop it's fetch with session and the best thing for this job inside a for-loop is continue keyword which will skip the current iteration and jumps to the next one 并且由于你输入了一个return语句,它将导致for-loop和function的退出,导致后续的图像捕获结束,触发disptach组通知,因为那时有相同的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
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM