Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can change/reassigned a constant value that Instantiated from a class

I've created the following class

class Person {
    var firstName: String
    var lastName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }


    func fullName() -> String {
        return "\(firstName) \(lastName)"
    }
}

Then I instantiated a constant value from the class

 let john = Person(firstName: "Johnny", lastName: "Applessed")

Question: Why I can change the content of the variable john? Isn't it a constant? Can someone explain that for me, thanks a lot.

john.firstName = "John"

print(john.firstName) // -> John
like image 622
SLN Avatar asked May 15 '16 13:05

SLN


3 Answers

As @Wain has said – it's due to the nature of reference types. The instance being a let constant only means you cannot assign a new reference to it – but says nothing about the actual mutability of the instance itself.

If you change your class to a struct, you'll see how the behaviour differs with value types, as changing a property changes the actual value of your Person – therefore you are unable to do so if it's a let constant. However I somewhat doubt you'll want to make your Person a struct, as two people with the same name shouldn't be considered to be the same person.

If you only wish your properties to be assigned upon initialisation (and then read-only for the lifetime of the instance), then I would recommend making them let constants (instead of making their setters private). This will ensure that you cannot even change their value from within your class, once assigned.

The rule is as long you give a property a value before the super.init() call – you can make it a let constant (in this case, you just have to assign them in the initialiser before using self).

class Person {
    let firstName: String
    let lastName: String

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }

...
like image 53
Hamish Avatar answered Nov 11 '22 09:11

Hamish


The class instance itself is a constant, so you can't change it to reference another instance, but the instance is mutable because it's properties are created as vars.

Change firstName to have a private setter and see what you can do:

private(set) var firstName: String
like image 44
Wain Avatar answered Nov 11 '22 09:11

Wain


When you're using a constant instance of a class in swift, doesn't mean you can't change the class attributes. It' means you can't instantiate a new object in this constant

let person = Person(firstName: "Johnny", lastName: "Appleseed")
person = Person(firstName: "John", lastName: "Appleseed") //--->It gets error: Cannor assign to value: 'person' is a 'let' constant

But you can create a constant inside class and set this values in the init

class Person {
    let firstName: String  
    let lastName: String 

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }


    func fullName() -> String {
        return "\(firstName) \(lastName)"
    }
}

//Tip: Don't init the class constants in declaration time or will get the same above error. Just init this constants at constructor/initialization of class.

And Now you have the expected result you want, even if create a 'var' instance of this object

var person = Person(firstName: "Johnny", lastName: "Appleseed")
person.firstName = "John"  //--->It gets error: Cannor assign to value: 'person' is a 'let' constant

person = Person(firstName: "John", lastName: "Snow")
person.firstName = "Johnny"  //--->It gets error: Cannor assign to value: 'person' is a 'let' constant

Your thinking was not wrong, but a little confuse cause you would be totally right if it was a struct instead a class.

like image 22
dede.exe Avatar answered Nov 11 '22 09:11

dede.exe