【问题标题】:Updating a property in a struct inside an array更新数组内结构中的属性
【发布时间】:2016-06-07 15:30:43
【问题描述】:

在我的应用中,我下载了大量 JSON。

然后我将其存储为结构数组并使用它来填充UITableView

结构的属性之一是图像的NSURL。另一个属性是可选的UIImage

该结构有一个变异函数downloadImage,它使用 URL 下载图像并将其存储在其属性中。

像这样……

struct SearchItem {
    // other properties...
    let iconURL: NSURL
    var icon: UIImage?

    mutating func downloadImage() -> Task<UIImage> {
        let tsc = TaskCompletionSource<UIImage>()

        NSURLSession.sharedSession().downloadTaskWithURL(iconURL) {
            (location, response, error) in
            if let location = location,
                data = NSData(contentsOfURL: location),
                image = UIImage(data: data) {
                self.icon = image
                tsc.setResult(image)
                return
            }

            tsc.setError(NSError(domain: "", code: 1, userInfo: nil))
        }.resume()

        return tsc.task
    }
}

我遇到的问题是这个。 (过去我一直被这件事难住了)。

我有一个数组[SearchItem] 用于填充表格视图。

cellForRow我有代码...if let searchItem = items[indexPath.row]...

然后它检查图像是否为 nil 并下载...

if let image = searchItem.icon {
    cell.imageView.image = image
} else {
    searchItem.downloadImage().continueOnSuccessWith(Executor.MainThread) {
        _ in
        tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
    }
}

但这永远不会将图像放入单元格中。这是因为SearchItem 是结构,所以pass-by-value。因此,我下载图像的搜索项与存储在数组中的搜索项 SearchItem 不同。

如何确保下载的图像随后存储到实际数组中的SearchItem 中?

【问题讨论】:

  • 简单:使用类:D
  • @jrturton 但是...这不是结构的理想用例吗?
  • 显然不行,否则它会工作
  • @jrturton 那么......如果我想在数组中存储数据时必须排除它们,那么结构到底是什么?
  • 经典:你不能从包含异步任务的函数/方法中返回一些东西。你需要一个——也是异步的——完成处理程序

标签: ios arrays swift struct


【解决方案1】:

使用类。

您在cellForRow 方法中获得了searchItem 的副本。无论您对此做什么,都只会对该副本执行。您真正想要的是将对该副本所做的更改应用于数组中的版本。

因此您需要引用语义,因此使用类。

如果您愿意,您可以将更新后的副本重新插入到原始数组中,但是除了一行额外的代码和可能的其他一些问题之外,这还能为您带来什么。

【讨论】:

  • 看来你是对的。 “结构,我很失望。”作为一个附带问题/评论,我写的 Swift 越多,似乎越无用的结构。他们有没有遇到与此相同的问题的用例?老实说,我写的 Swift 越多,我就越不喜欢它,并认为我还不如回到 Objective-C。 :(
  • 您可以将数组中的结构更新为您的“新”结构(突变后)
  • @AMomchilov 这就是我在答案的最后一行所说的
  • @jrturton 是的,只是为了强调一下:他想做的事情是可能的,但它的工作方式不同。
【解决方案2】:

结构是轻量级数据对象,不通过引用传递,而是在您 a) 将其传递给新函数 b) 尝试在块中访问它时根据需要复制自身。 Swift 中的数组的工作方式也与它们的 Obj-C 对应物略有不同。当您有一个类对象数组时,该数组将是一个引用类型,您将能够在此处实现您想要实现的目标。但另一方面,如果 Array 属于 Structs,则该数组会失去其引用语义,而是使用按值复制。

如果使用得当,这种差异非常强大,您可以极大地优化您的代码,使其运行得更快,减少由可变对象引用产生的错误,在代码的多个部分发生变化等。但这取决于您开发人员查看这些优化的收益在哪里有用,或者在哪里使用对象更有意义。

【讨论】:

  • 谢谢。我遇到的问题是,任何重要的在线参考资料似乎都与至少一个其他参考资料不一致。试图跟随他们会导致这样的情况。例如,一篇特定的文章说要为Person Data 使用结构。但是,如果您需要更改该人的姓名,您将更改原件的副本,因此这似乎是对 struct 的错误使用。老实说,除了超级微不足道的输入参数之外,我看不出它们有什么用处。但是我可以用一个元组来做类似的事情?!如果您可以推荐一个好的网站,您会喜欢一个吗?比
  • 我可以推荐这两个 WWDC 2015 Apple 开发视频:developer.apple.com/videos/play/wwdc2015/414 - developer.apple.com/videos/play/wwdc2015/408
  • @Fogmeister 值类型的好处是它们允许安全的变异。每当您调用库方法并传入您的结构时,您可以确保您的结构不会受到影响,并且不会发生意外的副作用。此外,这种最小化共享意味着您不必担心多线程应用程序中的同步问题。
  • @Fogmeister 引用语义最重要的缺陷是它巧妙地引入了意外数据共享的机会。很多时候,这是无意的。值类型将状态的影响降至最低,并使程序(通常)更容易推理。您永远不必考虑(还有哪些地方引用了这个对象?)
  • @AMomchilov 因此,结构的唯一正确用例是运行函数时。例如,当“上传新消息”时,您的消息可能是一个类,但您可以创建一个输入参数作为结构并将其传入以避免在消息排队时发生突变等...?
猜你喜欢
  • 2019-09-05
  • 2017-01-13
  • 2022-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 2023-03-18
  • 1970-01-01
相关资源
最近更新 更多