Swift,dispatch_group_wait不等

我正在尝试使用大中央调度等待文件完成下载之前继续。 这个问题是从这个衍生出来的: Swift(iOS),等待所有图像在返回之前完成下载 。

我只是试图找出如何让dispatch_group_wait(或类似)实际上等待,而不是在下载完成之前继续。 请注意,如果我使用NSThread.sleepForTimeInterval而不是调用downloadImage,它会等待很好。

我错过了什么?

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请求,而我正在使用不支持asynchronous操作的HTTP服务器(Swifter),所以我需要在返回响应之前等待完整下载完成(请参阅原始问题上面有更多的细节)。

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

为了完成这个,虽然,首先更改downloadImage以包含完成处理程序参数:

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

我已经做了一个完成处理程序,传回错误代码。 调整,你认为合适的,但希望它说明了这个想法。

但是,提供完成处理程序后,现在,当您执行下载时,您可以创build一个组,在启动每个下载之前“进入”组,在完成处理程序asynchronous调用时“离开”组。

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

 func downloadImages(imageFilesOnServer: [AdFileInfo], completionHandler: (Int) -> ()) { let group = dispatch_group_create() var downloaded = 0 for serverFile in imageFilesOnServer { dispatch_group_enter(group) print("Start downloading \(serverFile.fileName)") self.downloadImage(serverFile) { error in if error == nil { downloaded += 1 } dispatch_group_leave(group) } } dispatch_group_notify(group, dispatch_get_main_queue()) { completionHandler(downloaded) } } 

你会这样称呼它:

 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 3中…

 let dispatchGroup = DispatchGroup() dispatchGroup.enter() // do something, including background threads dispatchGroup.leave() dispatchGroup.notify(queue: DispatchQueue.main) { // completion code } 

https://developer.apple.com/reference/dispatch/dispatchgroup

代码正在做你正在告诉它。

调用dispatch_group_wait将会阻塞,直到dispatch_group_async调用完成。

调用dispatch_group_async内部的块将在for循环完成时完成。 这将会几乎立即完成,因为在downloadImage函数内完成的大部分工作是asynchronous完成的。

这意味着for循环很快结束,并且在完成任何实际下载之前,该块已经完成(并且dispatch_group_wait停止等待)。

我会使用dispatch_group_enterdispatch_group_leave而不是dispatch_group_async

我会改变你的代码,如下所示(未testing,可能是错别字):

 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_async(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) { dispatch_group_enter(group); 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)") } dispatch_group_leave(group); } } } 

这个改变应该做你需要的。 每次调用downloadImage将进入该组,并且不会离开该组,直到调用了下载完成处理程序。

使用这种模式,最后一行将在其他任务完成时执行。

 let group = dispatch_group_create() dispatch_group_enter(group) // do something, including background threads dispatch_group_leave(group) // can be called on a background thread dispatch_group_enter(group) // so something dispatch_group_leave(group) dispatch_group_notify(group, mainQueue) { // completion code }