Iterating over and modifying a collection of structs in Swift -
suppose have structs like:
struct tattoo { var imagetorso:uiimage? var imagetorsourl:url? var imagearms:uiimage? var imagearmsurl:url? } struct player { var name:string = "" var tattoos:[tattoo] = [] } struct team { var name:string = "" var players:[player] = [] }
now imagine have method passed in team value players. have iterate thru players , tattoos, download images , add them images variables on tattoos.
if use for in
loop, won't work because each part of loop gets copy of members of array it's iterating over. i.e.:
for player in team.players { tattoo in player.tattoos { if let url = tattoo.imagetorsourl { mynetframework.requestimage(from: url, completion: { image in tattoo.imagetorso = image } } } }
after doing iterations , completion blocks, still, original team
variable identical prior doing of this. because each tattoo inner loop got copy of in player's tattoos array.
now know can use &
pass structs reference in swift it's highly discouraged. know can use inout
don't copied when come functions, discouraged.
i know these made classes avoid behavior.
but supposing don't have choice in matter -- structs -- seems way like:
for p in 0...team.players.count-1 { t in 0...team.players[p].tattoos.count-1 { if let url = team.players[p].tattoos[t].imagetorsourl { mynetframework.requestimage(from: url, completion: { image in team.players[p].tattoos[t].imagetorso = image } } } }
this feels ugly , awkward, don't know how else around thing for in
loops give copy of thing you're iterating through.
can enlighten me, or how is in swift?
i think got point: "when requirement modifying data, better use class
instead." here question reference link you. why choose struct on class
struct
fast , can use them prevent creating huge, messy class. struct
provided immutable feature , make easier follow function programming
the significant benefit of immutable data free of race-condition , deadlocks. because read data , no worries problems caused changing data.
however, answer question, have few ways solve it.
1. renew whole data.
// first, need add constructors creating instances more easier. struct tattoo { var imagetorso:uiimage? var imagetorsourl:url? var imagearms:uiimage? var imagearmsurl:url? init(imagetorso: uiimage? = nil, imagetorsourl: url? = nil, imagearms: uiimage? = nil, imagearmsurl: url? = nil) { self.imagetorso = imagetorso self.imagetorsourl = imagetorsourl self.imagearms = imagearms self.imagearmsurl = imagearmsurl } } struct player { var name:string var tattoos:[tattoo] init() { self.init(name: "", tattoos: []) } init(name: string, tattoos: [tattoo]) { self.name = name self.tattoos = tattoos } } struct team { var name:string var players:[player] init() { self.init(name: "", players: []) } init(name: string, players: [player]) { self.name = name self.players = players } } player in team.players { tattoo in player.tattoos { if let url = tattoo.imagetorsourl { // catch old uiimage matching tattoo need updated. ({ (needchangeimage: uiimage?) -> void in mynetframework.requestimage(from: url, completion: { image in // reconstruct whole team data structure. let newplayers = team.players.map { (player) -> player in let newtattos = player.tattoos.map { (tattoo) -> tattoo in if needchangeimage == tattoo.imagetorso { return tattoo(imagetorso: image) } else { return tattoo } } return player(name: player.name, tattoos: newtattos) } team = team(name: team.name, players: newplayers) }) })(tattoo.imagetorso) } } }
these codes ugly, right? , there not awful performance issue caused going through whole data every network response; problem might causes retain cycle.
2. don't hold uiimage in data array.
redesign data structure, , use kingfisher download image synchronously.
kingfisher useful third party library. provides clean , simple methods use, , it's highly flexible.
let url = url(string: "url_of_your_image") imageview.kf.setimage(with: url)
however, think best way if don't want use kingfisher
change declaration struct
class
.
Comments
Post a Comment