Swift,dispatch_group_wait没有等待 [英] Swift, dispatch_group_wait not waiting

查看:507
本文介绍了Swift,dispatch_group_wait没有等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用grand central dispatch等待文件完成下载,然后再继续。这个问题是这个问题的衍生产品: Swift(iOS),等待所有图像在返回之前完成下载

I am trying to use grand central dispatch to wait for files to finish download before continuing. This question is a spin-off from this one: Swift (iOS), waiting for all images to finish downloading before returning.

我只是想弄清楚如何让dispatch_group_wait(或类似)实际等待,而不是在下载完成之前继续。请注意,如果我使用NSThread.sleepForTimeInterval而不是调用downloadImage,它等待就好了。

I am simply trying to find out how to get dispatch_group_wait (or similar) to actually wait and not just continue before the downloads have finished. Note that if I use NSThread.sleepForTimeInterval instead of calling downloadImage, it waits just fine.

我缺少什么?

class ImageDownloader {

    var updateResult = AdUpdateResult()

    private let fileManager = NSFileManager.defaultManager()
    private let imageDirectoryURL = NSURL(fileURLWithPath: Settings.adDirectory, isDirectory: true)

    private let group = dispatch_group_create()
    private let downloadQueue = dispatch_queue_create("com.acme.downloader", DISPATCH_QUEUE_SERIAL)

    func downloadImages(imageFilesOnServer: [AdFileInfo]) {

        dispatch_group_async(group, downloadQueue) {

            for serverFile in imageFilesOnServer {
                print("Start downloading \(serverFile.fileName)")
                //NSThread.sleepForTimeInterval(3) // Using a sleep instead of calling downloadImage makes the dispatch_group_wait below work
                self.downloadImage(serverFile)
            }
        }
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // This does not wait for downloads to finish.  Why?

        print("All Done!") // It gets here too early!
    }

    private func downloadImage(serverFile: AdFileInfo) {

        let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName)

        Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
        .response { _, _, _, error in
            if let error = error {
                print("Error downloading \(serverFile.fileName): \(error)")
            } else {
                self.updateResult.filesDownloaded++
                print("Done downloading \(serverFile.fileName)")
            }
        }
    }
} 

注意:这些下载是对HTTP POST请求的响应,我使用的HTTP服务器(Swifter)不支持异步操作,因此我需要等待完整下载完成才能返回响应(请参阅原始问题引用上面的更多细节)。

Note: these downloads are in response to an HTTP POST request and I am using an HTTP server (Swifter) which does not support asynchronous operations, so I do need to wait for the full downloads to complete before returning a response (see original question referenced above for more details).

推荐答案

当使用 dispatch_group_async 来调用方法时它们本身是异步的,只要所有异步任务都已启动,该组就会完成,但不会等待它们完成。相反,您可以在进行异步调用之前手动调用 dispatch_group_enter ,然后在异步调用完成时调用 dispatch_group_leave 。然后 dispatch_group_wait 现在将按预期运行。

When using dispatch_group_async to call methods that are, themselves, asynchronous, the group will finish as soon as all of the asynchronous tasks have started, but will not wait for them to finish. Instead, you can manually call dispatch_group_enter before you make the asynchronous call, and then call dispatch_group_leave when the asynchronous call finish. Then dispatch_group_wait will now behave as expected.

为实现此目的,首先要更改 downloadImage 包含完成处理程序参数:

To accomplish this, though, first change downloadImage to include completion handler parameter:

private func downloadImage(serverFile: AdFileInfo, completionHandler: (NSError?)->()) {
    let destinationPath = imageDirectoryURL.URLByAppendingPathComponent(serverFile.fileName)

    Alamofire.download(.GET, serverFile.imageUrl) { temporaryURL, response in return destinationPath }
        .response { _, _, _, error in
            if let error = error {
                print("Error downloading \(serverFile.fileName): \(error)")
            } else {
                print("Done downloading \(serverFile.fileName)")
            }
            completionHandler(error)
    }
}

我已经做了一个传递错误代码的完成处理程序。根据你的需要调整,但希望它能说明这个想法。

I've made that a completion handler that passes back the error code. Tweak that as you see fit, but hopefully it illustrates the idea.

但是,提供完成处理程序后,现在,当您进行下载时,您可以创建一个组,在开始每次下载之前输入该组,离开异步调用完成处理程序时的组。

But, having provided the completion handler, now, when you do the downloads, you can create a group, "enter" the group before you initiate each download, "leave" the group when the completion handler is called asynchronously.

但是 dispatch_group_wait 如果你不小心可以死锁,如果从主线程完成,可以阻止UI,等等。您可以使用 dispatch_group_notify 来实现所需的行为。

But dispatch_group_wait can deadlock if you're not careful, can block the UI if done from the main thread, etc. Better, you can use dispatch_group_notify to achieve the desired behavior.

func downloadImages(_ imageFilesOnServer: [AdFileInfo], completionHandler: @escaping (Int) -> ()) {
    let group = DispatchGroup()

    var downloaded = 0

    group.notify(queue: .main) {
        completionHandler(downloaded)
    }

    for serverFile in imageFilesOnServer {
        group.enter()

        print("Start downloading \(serverFile.fileName)")

        downloadImage(serverFile) { error in
            defer { group.leave() }

            if error == nil {
                downloaded += 1
            }
        }
    }
}

你会把它称为所以:

downloadImages(arrayOfAdFileInfo) { downloaded in
    // initiate whatever you want when the downloads are done

    print("All Done! \(downloaded) downloaded successfully.")
}

// but don't do anything contingent upon the downloading of the images here

对于Swift 2和Alamofire 3的答案,请参阅此答案的上一版本

For Swift 2 and Alamofire 3 answer, see previous revision of this answer.

这篇关于Swift,dispatch_group_wait没有等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆