草庐IT

swift - 从 Swift 3 中的 URL 列表下载内容的安全方法

coder 2023-09-09 原文

我有一个提供图像资源位置的 URL 列表。我设法找到了一个解决方案,但我觉得好像有比下面代码中显示的过程更好的方法。非常感谢有关如何改进此方法以异步检索图像的任何提示!

在我追加每个项目之后调用 completionHandler 并在异步 block 之外向索引 (i) 添加 1 并不奇怪,这意味着 while 循环在最后一个 url 完全完成之前迭代到下一个 url 项目处理过??

typealias imagesHandler = (_ images: [UIImage]) -> Void

func fetchImages(forUser user: User, completionHandler: imagesHandler?) {

    var images = [UIImage]()

    self.fetchImageUrlList(forUser: user) { (urlList: [URL]) in

        var i = 0

        while i <= urlList.count - 1 {
            let request = URLRequest(url: urlList[i])
            let dataTask = URLSession(configuration: .default).dataTask(with: request, completionHandler: {
                (data: Data?, response: URLResponse?, error: Error?) in
                guard error == nil else { return }
                guard let data = data else { return }
                guard let image = UIImage(data: data) else { return }

                images.append(image)
                completionHandler?(Array(Set(images)))
            })
            i += 1
            dataTask.resume()
        }
    }
}

最佳答案

中央调度中心

这是一个典型的多线程场景,Grand Central Dispatch 非常有用。

首先,为了使用 Grand Central Dispatch,请更新您的代码

func fetchImages(forUser user: User, completionHandler: ImagesHandler?) {

    var images = [UIImage]()
    let group = DispatchGroup()
    let serialQueue = DispatchQueue(label: "serialQueue")

    fetchImageUrlList(forUser: user) { urls in

        urls.forEach { url in
            // ***********************************************
            // tells the group that there is a pending process
            group.enter()
            URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in

                guard let data = data, let image = UIImage(data: data), error == nil else { group.leave(); return }

                // ***************************************************************************
                // creates a synchronized access to the images array
                serialQueue.async {
                    images.append(image)

                    // ****************************************************
                    // tells the group a pending process has been completed
                    group.leave()
                }
            }.resume()
        }

        group.notify(queue: .main) {
            // *****************************************************************************************
            // this will be executed when for each group.enter() call, a group.leave() has been executed
            completionHandler?(images)
        }
    }
}

群发

我用这一行创建了一个 GroupDispatch

let group = DispatchGroup()

你可以把它看成一个计数器。每次调用 group.enter() 时,计数器都会增加 1。每次调用 group.leave() 时,计数器都会减少 1

当然 DispatchGroup线程安全的

最后你写的一切

group.notify(queue: .main) {
    // HERE <------
}

将在 DispatchGroup 的内部计数器为 0 时执行。事实上,这意味着所有未决的“操作”都已完成。

串行队列

最后,您需要从不同线程访问images 数组。这是非常危险的。

所以我们使用串行队列。

serialQueue.async {
    images.append(image)
    group.leave()
}

我们附加到 Serial Queue 的所有闭包都会同时执行 1 次。因此可以安全访问 images 数组。

I haven't tested the code, please try and let me know ;)

关于swift - 从 Swift 3 中的 URL 列表下载内容的安全方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44316419/

有关swift - 从 Swift 3 中的 URL 列表下载内容的安全方法的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  5. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  6. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  7. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  8. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  9. ruby - 将数组的内容转换为 int - 2

    我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]

  10. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

随机推荐