Swift: how to assign an optional by reference - swift

Given the following code:
class Node {
var value: Int
var left: Node?
var right: Node?
init (value: Int) {
self.value = value
}
}
root = Node(value: 200)
Then the following code assigns a new node to root.left:
var temp1 = root
temp1!.left = Node(value: 400)
But the following does not assign a new node to root.right:
var temp2 = root!.right
temp2 = Node(value: 300)
Presumably in the last code snippet root!.right is assigned to temp2 by value, if so then is there a way of assigning it by reference?

This actually has nothing to do with optionals-- the same behavior would occur if the properties Node.left and Node.right were not optionals. Rather, this has to do with Swift class properties.
In your former example, you are pointing temp1 to the same class instance that root points to. Thus, root and temp1 are the same instance of Node, so when you set a property in one, it changes when you get it from the other.
In your latter example, you are getting the value of root!.right (which is currently nil) and setting it a new variable, temp2. You are creating a new class instance, so you end up with two separate class instances. When you assign to root.right, it will have no effect on temp2 (and vice versa).

Related

Why does optional chaining cause an overlapping accesses error?

struct someStruct {
var foo: String?
var bar: String?
}
var someOptional: someStruct? = someStruct()
someOptional?.bar = someOptional?.foo
This code causes the following error on the last line.
Overlapping accesses to 'someOptional', but modification requires exclusive access; consider copying to a local variable
If I replace the last line with the following, then the program works as expected.
let foo = someOptional?.foo
someOptional?.bar = foo
Why is the first example causing an error, and why does the alternate version (which I would assume to be identical) not?
Structs are value types, so when you do let foo = someOptional?.foo, the value of someOptional?.foo is copied into the local variable foo. Hence in your next line, someOptional?.bar = foo you don't access someOptional to get the value of foo anymore, but you access the value of the local variable directly.
This is why someOptional?.bar = someOptional?.foo is not equivalent to the above solution and why saving the value to a local variable resolves the overlapping accesses error.
The cause of the error is also the fact that you are using value types. In the line someOptional?.bar = someOptional?.foo you are mutating an instance property of someOptional and hence mutating the instance someOptional as well, while at the exact same time accessing another instance property of someOptional.
If someOptional was a reference type, you wouldn't get that error, see below:
class SomeClass {
var foo: NSString? // `NSString` is a reference type
var bar: NSString?
}
let someOptionalClass: SomeClass? = SomeClass()
someOptionalClass?.bar = someOptionalClass?.foo
let fooRef = someOptionalClass?.foo
someOptionalClass?.bar = fooRef

A Constant Array's Items Can(Cannot) Be Modified If They Are Reference(Value) Types. Why?

Please have a look at the following code and note the compiler error on the last line:
class C {
var value: Int
init( _ value: Int) { self.value = value }
}
let array1 = [C(1), C(2), C(3)]
array1[0].value = 4
struct S {
var value: Int
}
let array2 = [S(value: 1), S(value: 2), S(value: 3)]
array2[0].value = 4 // Error: Cannot assign to property: 'array2' is a 'let' constant
From the compiler error I want to conclude that the item at index 0 is being read from array2, modified, and then written back to array2. What else could produce the result that there is an attempt to modify array2? But, if my reasoning is correct, then why does the same thing not happen with array1?
Classes Are Reference Types
Unlike value types(struct), reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.
Please refer Apple documentation about Class & struct

Cannot assign to property: function call returns immutable value

Consider the following example.
struct AStruct{
var i = 0
}
class AClass{
var i = 0
var a: A = A(i: 8)
func aStruct() -> AStruct{
return a
}
}
If I try to mutate the the variable of a instance of class AClass it compiles successfully.
var ca = AClass()
ca.a.i = 7
But If I try to mutate the return value of aStruct method, the compile screams
ca.aStruct().i = 8 //Compile error. Cannot assign to property: function call returns immutable value.
Can someone explain this.
This is compiler's way of telling you that the modification of the struct is useless.
Here is what happens: when you call aStruct(), a copy of A is passed back to you. This copy is temporary. You can examine its fields, or assign it to a variable (in which case you would be able to access your modifications back). If the compiler would let you make modifications to this temporary structure, you would have no way of accessing them back. That is why the compiler is certain that this is a programming error.
Try this.
var aValue = ca.aStruct()
aValue.i = 9
Explanation
aStruct() actually returns a copy of the original struct a. it will implicitly be treated as a constant unless you assign it a var.
Try using a class instead of a struct, as it's passed by reference and holds onto the object, while a struct is passed by value (a copy is created).

assigned enum value doesn't change

I have a Player class which has an enum for changing the Player type. See init below.
init(playerType: PlayerType) {
self.playerType = playerType
spriteTexture = SKTexture(imageNamed: playerType.simpleDescription())
sprite = SKSpriteNode(texture: spriteTexture)
sprite.name = playerCategoryName
sprite.zPosition = 10
}
In the GameScene I change some player properties using the following method, but the enum value for player.playerType doesn't change when assigned. What is cause and how can I correct this?
func changePlayer (newPlayerType:Player.PlayerType) {
player.spriteTexture = SKTexture(imageNamed: newPlayerType.simpleDescription())
let action = SKAction.setTexture(player.spriteTexture)
player.sprite.runAction(action)
player.playerType == newPlayerType //this doesn't seem to work, player.playerType remains unchanged every time
println("raw value of newPlayerType is \(newPlayerType.rawValue)")
println("raw value of player.playerType is \(player.playerType.rawValue)")
}
You cannot assign a different case to an existing enum; you can make an existing enum change its own case, but not by assigning into it.
You can replace an enum instance (with one case) by another enum instance (with a different case) of the same enum, but only if the enum is a var reference, not a let reference.
== is the equality operator.
You're looking for = for assignment (note, only one equals sign).
Check your enum reference it should be var not let

Class Instantiation in Apple Swift [duplicate]

This question already has answers here:
What is the difference between `let` and `var` in Swift?
(32 answers)
Closed 8 years ago.
Why to instantiate a class I have to do it as a constant with let,
class car {
var type: Int?
var wheels: Int?
}
let auto = car()
I can use var as well:
var auto = car()
What is the difference?, thanks
A constant can only be assigned to, or initialized, once:
let constantAuto = car()
constantAuto.type = 1 // changing properties is fine
constantAuto.wheels = 4
constantAuto = car() // error - can't do this
whereas a variable can be assigned to multiple times:
var variableAuto = car()
variableAuto.type = 1 // changing properties is fine here too
// etc
// need to reset:
variableAuto = car()
Essentially, when you know you're only going to need to create the instance once, use let, so the compiler can be more efficient about the code it creates.
if you're using let you're defining a constant, whereas with var you're declaring a variable.
"A constant declaration defines an immutable binding between the constant name and the value of the initializer expression; after the value of a constant is set, it cannot be changed. That said, if a constant is initialized with a class object, the object itself can change, but the binding between the constant name and the object it refers to can’t."
from https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Declarations.html
to sum it up:
you can change the object a variable refers to, but you can't do that to a constant