简体   繁体   中英

Updating a property in a struct inside an array

In my app I download a load of JSON.

I then store that as an array of structs and use that to populate a UITableView .

One of the properties of the struct is an NSURL for an image. Another property is an optional UIImage .

The struct has a mutating function downloadImage which uses the URL to download the image and store it in its property.

Like this...

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
    }
}

The problem I'm having is this. (and I have been stumped by this in the past).

I have an array [SearchItem] that I use to populate the tableview.

In cellForRow I have the code... if let searchItem = items[indexPath.row]...

It then checks if the image is nil and downloads...

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

But this never goes through to put the image into the cell. This is because the SearchItem is struct and so pass-by-value . So the search item that I am downloading the image for is not the same SearchItem as the one stored in the array.

How can I ensure that the image that is downloaded is then stored into the SearchItem inside the actual array?

Use classes.

You're getting a copy of searchItem in your cellForRow method. Whatever you do to this, will be done only to that copy. What you actually want is for the changes you make to that copy to be applied to the version in the array.

Therefore you want reference semantics, therefore use classes.

You could dance around re-inserting the updated copy into the original array if you liked, but what does that gain you besides a line of extra code and probably some other problems.

Structs are lightweight data objects that are not passed by reference, but instead copies itself as needed when you a) pass it to a new function, b) try and access it in a block. Arrays in Swift also work slightly differently than their Obj-C counterparts. When you have an Array of class objects the array will be a reference type, and you'll be able to achieve what you're trying to achieve here. But on the other hand if the Array is of Structs the array looses its reference semantics and uses copy-by-value instead.

This difference is really powerful when used appropriately, you can greatly optimise your code, make it run faster, have less errors produced by mutable object references having changes happen in multiple parts of your code, etc. But it's up to you as a developer to see where the gains of these optimisations are useful or where it makes sense to use objects instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM