Cannot assign to property: function call returns immutable value - swift

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).

Related

Inout and NSMutableDictionary

If i run the following code in XCode 12 playground (Swift 5.3) I get the same result from two listings:
import Foundation
var dict = NSMutableDictionary()
dict["x"] = 42
func stuff(_ d: inout NSMutableDictionary) {
d["x"] = 75
}
stuff(&dict)
dump(dict) // x is 75
the other:
import Foundation
var dict = NSMutableDictionary()
dict["x"] = 42
func stuff(_ d: NSMutableDictionary) {
d["x"] = 75
}
stuff(dict)
dump(dict) // x is 75 still
As per the documentation here, the second listing should give me an error:
https://docs.swift.org/swift-book/LanguageGuide/Functions.html
But it works anyway.
Is this because the enforcement of these in-out rules is constrained to Swift only types, and Cocoa types are exempt?
This works not because Cocoa types are exempt, but because NSMutableDictionary is a class (as opposed to a struct), and the inout does not refer to what you might be thinking.
Unfortunately, the documentation you link to (and the more in-depth documentation on inout parameters it links to) doesn't make it clear what "value" really means:
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value
The following statement hints at it a little, but could be clearer:
You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified.
The "value" the documentation describes is the variable being passed as inout. For value types (structs), this is meaningful because every variable holding a value of those types effectively holds a copy of that value.
var a = MyGreatStruct(...)
var b = a
// a and b are not directly linked in any way
Passing a struct to a function normally copies the value into a new local variable (new variable = copy), whereas you can imagine inout giving you direct access to the original variable (no new variable).
What's not described is that the effect is identical for classes, which behave differently.
let a = MyGreatClass(...)
let b = a
// modifying `a` will modify `b` too since both point to the same instance
Passing a class to a function also copies the variable into a new local variable, but the copy isn't meaningful — both variables hold the same thing: a reference to the object itself in memory. Copying in that sense doesn't do anything special, and you can modify the object from inside of the function the same way you could from outside. inout for classes behaves the same way as for structs: it passes the original variable in by reference. This has no bearing on the majority of the operations you'd want to perform on the object anyway (though it does allow you to make the variable point to a different object from within the function):
var a = MyGreatClass("Foo")
// func foo(_ value: MyGreatClass) {
// value = MyGreatClass("Bar") // <- not allowed since `value` isn't mutable
// }
func foo(_ value: inout MyGreatClass) {
value = MyGreatClass("Bar")
}
print(ObjectIdentifier(a)) // <some pointer>
foo(&a)
print(ObjectIdentifier(a)) // <some other pointer>

Capturing a struct reference in a closure doesn't allow mutations to occur

I am trying to see if I can use structs for my model and was trying this. When I call vm.testClosure(), it does not change the value of x and I am not sure why.
struct Model
{
var x = 10.0
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
testClosure =
{
() -> () in
model.x = 30.5
}
}
}
var vm = ViewModel(model:&m)
m.x
vm.testClosure()
m.x
An inout argument isn't a reference to a value type – it's simply a shadow copy of that value type, that is written back to the caller's value when the function returns.
What's happening in your code is that your inout variable is escaping the lifetime of the function (by being captured in a closure that is then stored) – meaning that any changes to the inout variable after the function has returned will never be reflected outside that closure.
Due to this common misconception about inout arguments, there has been a Swift Evolution proposal for only allowing inout arguments to be captured by #noescape closures. As of Swift 3, your current code will no longer compile.
If you really need to be passing around references in your code – then you should be using reference types (make your Model a class). Although I suspect that you'll probably be able to refactor your logic to avoid passing around references in the first place (however without seeing your actual code, it's impossible to advise).
(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)
Instances of the closure will get their own, independent copy of the captured value that it, and only it, can alter. The value is captured in the time of executing the closure. Let see your slightly modified code
struct Model
{
var x = 10.0
mutating func modifyX(newValue: Double) {
let this = self
let model = m
x = newValue
// put breakpoint here
//(lldb) po model
//▿ Model
// - x : 30.0
//
//(lldb) po self
//▿ Model
// - x : 301.0
//
//(lldb) po this
//▿ Model
// - x : 30.0
}
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
model.x = 50
testClosure =
{ () -> () in
model.modifyX(301)
}
model.x = 30
}
}
let mx = m.x
vm.testClosure()
let mx2 = m.x
Here is what Apple says about that.
Classes and Structures
A value type is a type that is copied when it is assigned to a
variable or constant, or when it is passed to a function. [...] All
structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behaviour for that method. The method can then mutate (that is,
change) its properties from within the method, and any changes that it
makes are written back to the original structure when the method ends.
The method can also assign a completely new instance to its implicit
self property, and this new instance will replace the existing one
when the method ends.
Taken from here

Lazy load MirrorType

Let's say I have something like this
struct A {
lazy var b: String = { return "Hello" }()
}
If I try to reflect struct A and access the value for b through its MirrorType like so:
var a = A()
var r = reflect(a)
for i in 0..r.count {
let (n, m) = r[i]
println("\(m.value)")
var c = a.b
println("\(m.value)")
}
I get nil in the console both times. Note that the underlying value type is Swift.Optional<Swift.String>, and the variable name is somewhat confusingly b.storage. Is there a way to access the underlying value of a lazy-loaded variable using reflection or initialize it from its MirrorType or am I stuck waiting for someone to write a first-class reflection api for Swift?
The MirorType is very limited in it's functionality. Besides that it's replaced by other functionality in Xcode 7 beta 4.
The point in your case is that the property has not been used yet. So it's actually still nil. The only way to make it not nil is by accessing the property by getting it's value. Unfortunately in Swift you can not do that by executing .valueForKey("propertyName")
If you are looking for a reflection library that is trying to get as much as possible out of Swift, then have a look at EVReflection

Access Class In A Dictionary - Swift

I am now writing a program involves class and dictionaries. I wonder how could I access a class's values inside a dictionary. For the code below how do I access the test1 value using the dictionary. I have tried using dict[1].test1but it doesn't work.
class test {
var tes1 = 1
}
var refer = test()
var dict = [1:refer]
There are a few problems with the line dict[1].test1:
Firstly, the subscript on a dictionary returns an optional type because there may not be a value for the key. Therefore you need to check a value exists for that key.
Secondly, in your class Test you've defined a variable tes1, but you're asking for test1 from your Dictionary. This was possibly just a type-o though.
To solve these problems you're code should look something like this:
if let referFromDictionary = dict[1] {
prinln(referFromDictionary.test1)
}
That's because the subscript returns an optional, so you have to unwrap it - and the most straightforward way is by using optional chaining:
dict[1]?.tes1
but you can also use optional binding:
if let test = dict[1] {
let value = test.tes1
}

Swift: when should I use "var" instead of "let"?

As Apple Documentation says:
Use let to make a constant and var to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once. This means you can use constants to name a value that you determine once but use in many places.
Let's consider some class:
class A {
var a = [String]()
}
Since array a is mutable, it's defined via var. But what if we consider class B, where instance of A is property?
class B {
let b = A()
}
Even if b is mutable, let keyword will be ok, because reference won't be changed. On the other hand, var will be ok too, because content of b can be changed. What should I pick in this example - let or var?
Use let whenever you can. Use var when you must. Making things immutable makes a lot of bugs impossible, so it should be your default choice. As much as possible, set all your values in init and never change them. Similarly, you should use struct when you can, and class when you must (though in my experience this is harder to achieve than using let).
So in your example, if you cannot set A.a during initialization, then yes, it should be var. But there is no need in your example to use var for B.b. (And there's no reason in either example to use class, at least in the way you've presented the question.)
Let's give these better names to help with our reasoning. Let's say
class Head {
var hairs = [String]()
}
class Person {
let head = Head()
}
In this example, a Person has exactly one head, and for the lifetime of each Person, his/her head will always been that same head. However, that head's hairs can grow in, fall out, or otherwise change. Person's ownership of this head has no bearing on the Head's relationship to its hairs.
As Rob mentioned, you should always use let unless you have a good reason not to. Immutability is your friend when it comes to reasoning about program behavior.
Use let when your object does not change its value after has been set a value.
Use var if your object can change its value more than 1 time.
'let' is for constance. 'var' is for something variable.
However, constant restriction is only applied to the object but not its attributes if the object is an instance of class (value are passed by reference). Depending on type of those attributes (constant or variable), we can change their value after. This is not true for structure.
For example:
class VideoMode {
var interlaced = false
var frameRate = 0.0
var name: String?
}
in a function, you declare
let vm = VideoMode()
print("starting framerate is \(vm.frameRate)") // -> print starting framerate is 0.0
vm.frameRate = 20.0
print("framerate now is \(vm.frameRate)") // -> print framerate now is 20.0
//we can change .frameRate later to 10.0, there is no compile error
vm.frameRate = 10.0
print("framerate now is \(vm.frameRate)") // -> print framerate now is 10.0
When you declare a variable with var, it means it can be updated, it is variable, it’s value can be modified.
When you declare a variable with let, it means it cannot be updated, it is non variable, it’s value cannot be modified.
var a = 1
print (a) // output 1
a = 2
print (a) // output 2
let b = 4
print (b) // output 4
b = 5 // error "Cannot assign to value: 'b' is a 'let' constant"
Let us understand above example: We have created a new variable “a” with “var keyword” and assigned the value “1”. When I print “a” I get output as 1. Then I assign 2 to “var a” i.e I’m modifying value of variable “a”. I can do it without getting compiler error because I declared it as var.
In the second scenario I created a new variable “b” with “let keyword” and assigned the value “4”. When I print “b” I got 4 as output. Then I try to assign 5 to “let b” i.e. I’m trying to modify the “let” variable and I get compile time error “Cannot assign to value: ‘b’ is a ‘let’ constant”.
Source: https://thenucleargeeks.com/2019/04/10/swift-let-vs-var/