当前位置: 首页 > 面试题库 >

Swift,dispatch_group_wait不等待

上官自明
2023-03-14
问题内容

我正在尝试使用中央集中调度来等待文件完成下载再继续。这个问题是从以下问题衍生出来的: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现在将按预期方式运行。

但是,要实现此目的,请首先进行更改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用来实现所需的行为。

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的iTu​​nesU中的“开发适用于iPhone和iPad的ios7应用程序”中复制斯坦福Matchismo游戏。 在第3讲幻灯片的第77页上,它显示了使用,这不是Swift上的选项。Swift文档示例显示了一个具有数组的示例,但是我不知道如何使Interface Builder将多个插座连接到同一个/ Array。 有人知道如何做到这一点吗? 我知道我可以创建1

  • 问题内容: 是否有斯威夫特的本地等效于? 假设我有两个包含键和值的数组,并想将它们放入字典中。在Objective-C中,我会这样做: 当然,我可以遍历两个数组的计数器,使用a 并逐步添加内容。但这似乎不是一个好的解决方案。同时使用和可能是更好的迭代方式。但是,这种方法意味着拥有可变的字典,而不是不可变的字典。 问题答案: 从 Swift 4开始, 您可以直接从键/值对序列创建字典: 这假定所有键

  • 在Swift中,字符串中的的等价物是什么 我尝试了以下操作,但收到一条错误消息

  • 问题内容: 我发现Swift数值特别笨拙,因为在现实生活中经常发生这种情况,我必须就CGRect和CGPoint与Cocoa Touch交流(例如,因为我们在谈论某事或)。 CGFloat vs.双 考虑下面的来自UIViewController子类的看上去无辜的代码: 该代码无法编译,最后一行出现通常的神秘错误: 找不到接受提供的参数的’*’的重载 我现在已经确定,这个错误表明类型之间的某种阻抗

  • 问题内容: 我已经用谷歌搜索,但无法找出什么是等效的。 这是我唯一能找到的(Swift的替代方法responsesToSelector:),但在我的情况下并没有太大意义,因为它检查了委托的存在,我没有委托,我只想检查是否存在新的API在设备上运行时是否返回,如果不是,则返回原先的api版本。 问题答案: 如前所述,在 大多数情况下,您可以使用可选的unwrapper运算符来实现所需的功能。这样,当

  • 问题内容: 我不确定如何处理这种情况,因为我是iOS开发和Swift的新手。我正在像这样执行数据获取: 我的loadShows()函数解析从加载到UIWebView的网站中获取的大量数据。问题是我在loadShows函数中有一个等待10秒钟左右的计时器。这允许页面中的javascript在开始解析数据之前完全加载。我的问题是完成处理程序在我的loadShows()之前完成。 我想做的是为“ isC