在我的应用中,我下载了一大堆JSON。
然后我将其存储为结构数组,并使用它来填充UITableView
。
结构的一个属性是图像的NSURL
。另一个属性是可选的UIImage
。
该struct有一个变异函数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]
,用于填充tableview。
在cellForRow
我有代码...... if let searchItem = items[indexPath.row]...
然后检查图像是否为零并下载...
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
?
答案 0 :(得分:4)
使用课程。
您在searchItem
方法中获得了cellForRow
的副本。无论你做什么,都只会对那份副本进行。您真正想要的是您对该副本所做的更改将应用于阵列中的版本。
因此,您需要引用语义,因此请使用类。
如果你喜欢的话,你可以跳过将更新后的副本重新插入到原始数组中,但除了一系列额外的代码和其他一些问题之外,它还能带给你什么。
答案 1 :(得分:3)
结构是轻量级数据对象,不通过引用传递,而是在a)将其传递给新函数时根据需要复制自身,b)尝试在块中访问它。 Swift中的数组与Obj-C对应的工作方式略有不同。如果您有一个类对象Array,那么该数组将是一个引用类型,您将能够实现您在此处尝试实现的目标。但另一方面,如果Array是Structs,则数组会丢失其引用语义并使用copy-by-value。
如果使用得当,这种差异非常强大,您可以大大优化代码,使其运行更快,可变对象引用产生的错误更少,代码的多个部分发生变化等等。但是它作为开发人员,您可以了解这些优化的好处在哪里有用,或者在哪里使用对象更有意义。