I have this simple code:
for index in 0..<people.count {
var person = people[index]
var home = homes[index]
person.home = home
println("\(person.home)")
}
for index in 0..<people.count {
var person = people[index]
println("\(person.home)")
}
Person:
struct Person: Deserializable {
var home: Home?
init() { }
init(data: [String : AnyObject]) {
home <-- data["home"]
}
}
In the println statement in first loop, it assigns the home to it's respective person and prints the proper home.
In the second loop, it prints nil (back to normal). It's almost as if the first loop has no effect.
I have no idea how to debug this. Please help
You do not give any information about what person is. But suppose it is a struct. Then I would not expect there to be any effect on the people array's persons, because the person in var person = people[index] is a copy.
So in that case you would need an extra line in your first loop, writing the altered person back into the array:
people[index] = person
Related
I'm working on a game where I store my player objects in an array. I want to give these players a hand of cards and I do so in the following way:
var players = [Player]()
mutating func deal(count: Int) {
for var player in players {
for _ in 1...count {
if let card = deck.draw(){
player.addCard(card: card)
}
}
if (player.id==1){print(player)}
}
print(players[0])
}
struct Player{
var id : Int
private(set) var randomValue: Int = 0
private(set) var hand = [PlayingCard]()
init(id: Int) {
self.id = id
randomValue = 100.arc4random
}
mutating func addCard(card: PlayingCard){
hand.append(card)
}
}
The problem is the hand is not actually being updated. Printing the player at index 0 (player.id == 1) inside the for loop gives me exactly what I expect. However, the print outside the loop has an empty hand and the random number is different. So I know the loop is creating a new object since init is called. Why is this happening and how can I edit the actual object in players array?
player is struct, when you iterate players - you get a copy of the players in the array.
You update it, print it and see the correct result, but it's scope is for the for loop only.
Either make player a class or update your array with the new copy of the player.
Suppose you have some 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 that you have a method that was passed in a Team value with some players. You have to iterate thru the players and their tattoos, then download the images and add them into the images variables on the tattoos.
If you use a for in loop, then it won't work because each part of the loop gets a copy of the members of the array it's iterating over. I.e.:
for player in team.players {
for tattoo in player.tattoos {
if let url = tattoo.imageTorsoURL {
MyNetFramework.requestImage(from: url, completion: { image in
tattoo.imageTorso = image
}
}
}
}
After doing all the iterations and completion blocks, still, the original team variable is identical to what it was prior to doing any of this. Because each tattoo that the inner loop got was a copy of what is in the player's tattoos array.
Now I know you can use & to pass structs by reference in Swift but it's highly discouraged. As well I know you can use inout so they don't get copied when they come into functions, which is also discouraged.
I also know these could be made classes to avoid this behavior.
But supposing I don't have a choice in the matter -- they are structs -- it seems the only way to do this is something like:
for p in 0...team.players.count-1 {
for 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 and awkward, but I don't know how else to get around this thing where for in loops give you a copy of the thing you're iterating through.
Can anyone enlighten me, or is this just how it is in Swift?
I think you already got the point: "When your requirement will be modifying the data, you better to use class instead."
Here is the question reference link for you. Why choose struct over class
struct is fast and you can use them to prevent creating a huge, messy class. struct provided the immutable feature and make us easier to follow the Function Programming
The most significant benefit of immutable data is free of race-condition and deadlocks. That because you only read the data and no worries about the problems caused by changing data.
However, to answer your question, I have few ways to solve it.
1. Renew whole data.
// First, we need to add constructors for 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
}
}
for player in team.players {
for tattoo in player.tattoos {
if let url = tattoo.imageTorsoURL {
// Catch old UIImage for matching which Tattoo need to be 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 are ugly, right? And there will not only be awful performance issue caused by going through whole data every network response; another problem is that might causes the retain cycle.
2. Don't hold UIImage in the data array.
Redesign your data structure, and use Kingfisher to help us download image synchronously.
Kingfisher is useful third party library. It provides clean and simple methods to use, and it's highly flexible.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
However, I think the best way for you if you don't want to use Kingfisher is to change your declaration from struct to class.
Unfortunately that's the nature of struct and Swift doesn't offer a way for you modify the collection in-place while iterating over it. But can user enumerated() to get both the index and the element when iterating:
for (p, player) in team.players.enumerated() {
for (t, tattoo) in player.tattoos.enumerated() {
if let url = tattoo.imageTorsoURL {
MyNetFramework.requestImage(from: url, completion: { image in
team.players[p].tattoos[t].imageTorso = image
}
}
}
}
Assume I have the following objects:
class Dog {
let name: String
var stats: Stats?
func doSomething() { /*...*/ }
}
struct Stats {
var weight: Double
var age: Int
func ageEquivalence() -> Int { /*...*/ }
}
... and that I want to perform some calculation on a Dog instance. I want to do this atomically, however. In other words, I want to guarantee that either all code is executed, or no code is executed.
To clarify the problem, first, let's assume that Stats were a class. In that case, this problem is easy to solve:
func updateAge(_ dog: Dog) {
guard let stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
It's either going to going to execute lines A-C if stats exists, or do nothing.
Now let's go back to the original problem, assume Stats is a struct again. If I try to run the code above, first I must change let stats to var stats, otherwise the compiler will complain that I'm trying to update a constant.
func updateAge(_ dog: Dog) {
guard var stats = dog.stats else {
return
}
stats.age += 1 // A
dog.doSomething() // B
sendNotification(stats.ageEquivalence()) // C
}
But now we run in to the problem that stats is a copy of dog.stats so when I update the age on line A, I'm not actually modifying the original dog's age.
Our next approach might look like this:
func updateAge(_ dog: Dog) {
dog.stats?.age += 1 // A
dog.doSomething() // B
if let stats = dog.stats {
sendNotification(stats.ageEquivalence()) // C
}
}
The problem with this approach is that if doSomething has a side effect which sets stats = nil, we would have run lines A & B, but not the C.
What's the best way to solve this problem?
It seems that the crux of the problem is this line:
dog.stats?.age += 1
In order to modify a struct, we must act on the original object, meaning we must use optional chaining. This cannot be used in conjunction with a guard var since that would create a copy of the original struct, and the original struct might be changed due to a side effect at any point of the execution.
class Book: Object {
// (…)
var readingSessions: [ReadingSession] {
return linkingObjects(ReadingSession.self, forProperty: "book")
}
}
class ReadingSession: Object {
// (…)
var book: Book?
var aComputedProperty: Int {
print(self) // Prints ReadingSession { book = Book { (…) } (…) }
print(self.book) // Prints nil
// (…)
}
}
The code pretty much says it all. If I print self from within my computed property it prints the related object among the other properties like I expect. If I just try to get that object, though, it returns nil. Is that supposed to happen? Am I doing something wrong?
Thanks in advance,
Daniel
Edit: It seems the problem isn't actually related to computed properties. Just calling this from a regular view controller gives me the same problem:
let session = ReadingSession()
// (…)
print(session) // This returns every property, including the Book
print(session.book) // This returns nil
It doesn't happen always, though. On some parts of my code I do the same thing and it works as expected, in other parts this happens. So I guess I'm just making some silly mistake somewhere, but I have no idea what could be causing this.
let realm = try! Realm()
let sessions = realm.objects(ReadingSession).filter("book = %#", user.selectedBook!).sorted("date")
let session = sessions[indexPath.row]
print(session.book) // prints nil
session.book = user.selectedBook!
print(session.book) // prints the correct book
I don't understand! I'm filtering the sessions based on a Book. When I print the filtered session's book shouldn't it return the book I used to filter it? But it only works if I set its book property to that same book again!
You have to declare the user property as dynamic var to make this work:
class ReadingSession: Object {
dynamic var book: Book?
//...
}
I have this class named Meal
class Meal {
var name : String = ""
var cnt : Int = 0
var price : String = ""
var img : String = ""
var id : String = ""
init(name:String , cnt : Int, price : String, img : String, id : String) {
self.name = name
self.cnt = cnt
self.price = price
self.img = img
self.id = id
}
}
and I have an array of Meal :
var ordered = [Meal]()
I want to duplicate that array and then do some changes to the Meal instances in one of them without changing the Meal instances in the second one, how would I make a deep copy of it?
This search result didn't help me
How do I make a exact duplicate copy of an array?
Since ordered is a swift array, the statement
var orderedCopy = ordered
will effectively make a copy of the original array.
However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.
If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:
struct Meal {
...
From the Apple book:
Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.
To improve on #Kametrixom answer check this:
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
You either have to, as #MarioZannone mentioned, make it a struct, because structs get copied automatically, or you may not want a struct and need a class. For this you have to define how to copy your class. There is the NSCopying protocol which unifies that on the ObjC world, but that makes your Swift code "unpure" in that you have to inherit from NSObject. I suggest however to define your own copying protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
which you can implement like this:
class Test : Copying {
var x : Int
init() {
x = 0
}
// required initializer for the Copying protocol
required init(original: Test) {
x = original.x
}
}
Within the initializer you have to copy all the state from the passed original Test on to self. Now that you implemented the protocol correctly, you can do something like this:
let original = Test()
let stillOriginal = original
let copyOriginal = original.copy()
original.x = 10
original.x // 10
stillOriginal.x // 10
copyOriginal.x // 0
This is basically the same as NSCopying just without ObjC
EDIT: Sadly this yet so beautiful protocol works very poorly with subclassing...
A simple and quick way is to map the original array into the new copy:
let copyOfPersons: [Person] = allPersons.map({(originalPerson) -> Person in
let newPerson = Person(name: originalPerson.name, age: originalPerson.age)
return newPerson
})
The new Persons will have different pointers but same values.
Based on previous answer here
If you have nested objects, i.e. subclasses to a class then what you want is True Deep Copy.
//Example
var dogsForAdoption: Array<Dog>
class Dog{
var breed: String
var owner: Person
}
So this means implementing NSCopying in every class(Dog, Person etc).
Would you do that for say 20 of your classes? what about 30..50..100? You get it right? We need native "it just works!" way. But nope we don't have one. Yet.
As of now, Feb 2021, there is no proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your class conforms to codable
class Dog: Codable{
var breed : String = "JustAnyDog"
var owner: Person
}
Create this helper class
class DeepCopier {
//Used to expose generic
static func Copy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need true deep copy of your object, like this:
//Now suppose
let dog = Dog()
guard let clonedDog = DeepCopier.Copy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
clonedDog.breed = "rottweiler"
//Also clonedDog.owner != dog.owner, as both the owner : Person have dfferent memory allocations
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our object. Just make sure all your Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.