我正在尝试使用中央集中调度来等待文件完成下载再继续。这个问题是从以下问题衍生出来的: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请求的响应,并且我使用的是不支持异步操作的HTTP服务器(Swifter),因此在返回响应之前,我确实需要等待完整的下载完成(请参阅引用的原始问题)上方以获取更多详细信息)。
当使用dispatch_group_async本身是异步的方法进行调用时,该组将在所有异步任务启动后立即完成,但不会等待它们完成。相反,您可以dispatch_group_enter在进行异步调用之前手动调用,然后dispatch_group_leave在异步调用完成时进行调用。然后dispatch_group_wait现在将按预期方式运行。
dispatch_group_async
dispatch_group_enter
dispatch_group_leave
dispatch_group_wait
但是,要实现此目的,请首先进行更改downloadImage以包括完成处理程序参数:
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) } }
我已经完成了一个传回错误代码的完成处理程序。您可以根据自己的喜好对其进行调整,但希望它可以说明这一想法。
但是,已经提供了完成处理程序,现在,当您进行下载时,您可以创建一个组,在启动每次下载之前“输入”该组,在异步调用完成处理程序时“离开”该组。
但是,dispatch_group_wait如果您不小心,则可能导致死锁;如果从主线程完成操作,则可能会阻塞UI,等等。更好的是,您可以dispatch_group_notify用来实现所需的行为。
dispatch_group_notify
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