【问题标题】:Append images in array in sequence将图像按顺序添加到数组中
【发布时间】:2016-07-21 05:27:59
【问题描述】:

我想在下载后按顺序添加数组中的图像。我在一张一张下载后将图像附加到数组中,但它们不是按顺序排列的。谁能告诉我什么是最好的方法来做到这一点。

var queue: NSOperationQueue = {
    let _queue = NSOperationQueue()
    _queue.maxConcurrentOperationCount = 4
    return _queue
}()

var imageArrayNsData : [NSData] = []

let session = NSURLSession.sharedSession()

@IBAction func didClickOnStart(sender: AnyObject) {

    queue.cancelAllOperations()

    let completionOperation = NSBlockOperation() {
        print("all done")
    }

    for (index, imageURL) in imageURLs.enumerate() {
        let operation = ImageNetworkOperation(session: session, urlString: imageURL) { image, response, error in

            let dtA : NSData = NSData(data: UIImageJPEGRepresentation(image!, 0.75)!)
            self.imageArrayNsData.append(dtA)
            print("JPEG download\(index)")
        }

        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }

    NSOperationQueue.mainQueue().addOperation(completionOperation)        
}

结果输出:

JPEG 下载0
JPEG 下载2
JPEG 下载1
JPEG 下载3
全部搞定

【问题讨论】:

  • 请澄清您所说的“按顺序”是什么意思。
  • 您可以选择GCD,它非常易于使用,并且具有串行队列、并发队列等功能。这是相同的教程 - raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1.
  • 我的意思是按顺序排列,因为 url 在数组中。例如 10 个 url 的图像在数组中。图像需要保存在与其网址相同的索引号中,保存在特定索引处。但是由于异步图像是随机下载的。不按顺序。我需要按顺序保存所有图像,因为我需要将它们与标题一起使用。我正在下载这些图像。 http://www.wcvb.com/9849860?format=rss_2.0&view=feed
  • 在不了解很多 GDC 内容的情况下,最简单的方法是在下载 UIImage 之后,在具有 imageURL 索引的对象中创建它,然后在之后随机附加将sort 与索引一起使用,那么你得到了相同的顺序:)
  • 感谢 Tj3n。我已经尝试这样做了。

标签: ios swift swift2 nsurlsession nsoperationqueue


【解决方案1】:

您应该更改您的模型,以使下载图像的顺序无关紧要。例如,您有一组图像 URL 字符串:

var imageURLs: [String]

因此,您的 NSData 应该存储在由该 URL 字符串键入的字典(或 NSCache)中:

var imageData = [String: NSData]()

那么当你下载数据的时候,你就可以更新这个字典了:

self.imageData[imageURL] = dtA

然后,当您以后需要检索此数据时,可以使用 imageURL,例如:

let data = imageData[imageURLs[index]]

或者您可以将其定义为[Int: NSData] 并使用数字索引作为键。但想法是您可以使用字典,然后您收到响应的顺序无关紧要,但您仍然可以享受执行并发请求的性能优势。


我的建议是这样的:

var imageData = [String: NSData]()

@IBAction func didClickOnStart(sender: AnyObject) {

    queue.cancelAllOperations()

    let completionOperation = NSBlockOperation() {
        print("all done")
    }

    for (index, imageURL) in imageURLs.enumerate() {
        let operation = DataOperation(session: session, urlString: imageURL) { data, response, error in
            guard let data = data where error == nil else { return }
            guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else { return }

            NSOperationQueue.mainQueue().addOperationWithBlock {
                self.imageData[imageURL] = data
            }
            print("JPEG download\(index)")
        }

        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }

    NSOperationQueue.mainQueue().addOperation(completionOperation)        
}

然后像这样访问它:

if let data = imageData[imageURLs[index]], let image = UIImage(data: data) {
    // use `image` here
}

或者

var imageData = [Int: NSData]()

@IBAction func didClickOnStart(sender: AnyObject) {

    queue.cancelAllOperations()

    let completionOperation = NSBlockOperation() {
        print("all done")
    }

    for (index, imageURL) in imageURLs.enumerate() {
        let operation = DataOperation(session: session, urlString: imageURL) { data, response, error in
            guard let data = data where error == nil else { return }
            guard let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 else { return }

            NSOperationQueue.mainQueue().addOperationWithBlock {
                self.imageData[index] = data
            }
            print("JPEG download\(index)")
        }

        completionOperation.addDependency(operation)
        queue.addOperation(operation)
    }

    NSOperationQueue.mainQueue().addOperation(completionOperation)        
}

然后像这样访问它:

if let data = imageData[index], let image = UIImage(data: data) {
    // use `image` here
}

注意,ImageNetworkOperation 只是调用DataOperation 来获取NSData,然后将其转换为UIImage。如果你真的想要原来的NSData,我建议绕过ImageNetworkOperation,直接调用DataOperation,如上所示。

【讨论】:

  • 感谢 Rob 再次帮助我。有用。现在我尝试对imageData = [String: NSData]() 进行排序,因为稍后我需要在tableViewCell 中使用这些带有标题的图像
  • @ZAFAR007 - 字典的美妙之处在于您不必对其进行排序。你可以使用if let data = imageData[imageURLs[indexPath.row]], let image = UIImage(data: data) { ... }
  • Rob- 对不起。我知道AlamofireImage 库,但由于它的库文件需要更多的存储空间,所以我没有使用它。我也不想直接在 tableViewCell 中显示图像,因为我首先将这些图像保存在 NSUserDefualts 中,然后在从 NSUserDefaults 检索图像后显示在 tableViewCell 中。因为我想让我的应用也可以离线阅读。我也尝试使用 dispatch_get_global_queue 进行同步下载,按顺序下载图像,但由于 dipatch 不可取消,我使用您的 NSOperationQueue 解决方案。
  • @ZAFAR007 - 让我们continue this discussion in chat
  • 嗨,罗伯。如果您有空,请检查我的新问题。 :-)。我还没有得到任何帮助。我认为我的代码中有一点错误。谢谢。 stackoverflow.com/questions/38561293/…
【解决方案2】:

我不确定,但我认为,您无法控制下载顺序.. 意味着所有请求都通过管道传输到服务器(无论您创建 URL 对象的顺序如何)。您需要做的是,您必须维护包含 url 到实际数据映射的可变数组或字典,然后等到所有 url 已完全下载。然后以已知的顺序迭代。

【讨论】:

  • 你能举个简单的例子吗?
  • 您应该等待下载所有 url(将其存储在一个可变数组中,一旦完成,然后对其进行排序)
  • 或者如果你想一个一个下载然后看@Andrey的回答
  • 谢谢Suraj Sukale。下载后我尝试排序。
  • 是的兄弟..我认为这是更好的解决方案..(使用@Rob解决方案)如果答案有帮助,那么您可以投票给我的答案
【解决方案3】:

尝试:

var previousOperation : NSOperation! = nil

    for (index, imageURL) in imageURLs.enumerate()
    {
        let operation = ImageNetworkOperation(session: session, urlString: imageURL)
        { image, response, error in

            let dtA : NSData = NSData(data: UIImageJPEGRepresentation(image!, 0.75)!)
            self.imageArrayNsData.append(dtA)
            print("JPEG download\(index)")
        }

        completionOperation.addDependency(operation)

        if (previousOperation != nil)
        {
            operation.addDependency(previousOperation)
        }

        previousOperation = operation
        queue.addOperation(operation)
    }

    NSOperationQueue.mainQueue().addOperation(completionOperation)

这是一个非常快速和粗略的解决方案,当然可能有更好的解决方案。出现此问题是因为操作队列中的操作是同时执行的,并且不能保证按照它们开始的顺序完成。通过将依赖项添加到循环中的先前操作,您可以确保它们按顺序执行

【讨论】:

  • 也许这比同时下载所有文件并尝试重新排序要好
  • 我试过了,但结果相同JPEG download0 JPEG download2 JPEG download1 JPEG download3 all done
  • 这是我的代码,您可以在给我解决方案时尝试修复它。谢谢。 https://drive.google.com/open?id=0B29ka7gbDAYxaXdsX05KTUFzYzA
  • @ZAFAR007 查看我更新的代码。有用。但正如 Rob 所说,这个解决方案远非完美。我建议采用他的方法
  • 感谢安德烈帮助我。它工作,但有一个问题。如果我在下载过程中取消NSOperationQueue,则会在let dtAfatal error: unexpectedly found nil while unwrapping an Optional value 出现错误。我想我需要使用 Rob 方法。下载图像后,我需要对Dictionary 进行排序,因为从这种方法中我无法对数组进行排序,因为它们在没有键(url)的情况下追加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 2013-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多