Swift——(六)Swift中的值类型

    在Swift中,结构体和枚举类型为值类型(Structures and Enumerations),在Stack Overflow上有这样一个问题:结构体中的可变属性只有在使用mutating关键字以后才能被内置的方法修改(若是是不可变属性确定不能够修改了,这就不用纠结了,不过在Swift中,还有一个例外,那就是构造器Initialization对于结构体和类的常量实例属性能够在构造器中进行设置,这感受有点违背常量属性的含义,仔细想一想也能够理解,由于实例化的时候,最后调用的是构造器,因此在构造器以后,实例属性的值才肯定)。swift

    @Author: twlkyao 转载或者引用请保留此行。ide

    这里梳理下原因,有什么不对的地方还望指出,在解决这个问题以前,有两个概念须要解释。spa

    首先,一个值是否是能够修改并不在于它是什么类型(类仍是结构体),而在于它的存储类型(常量仍是变量)。只有变量才能够进行修改。.net

    而后,值类型能够理解为每一个属性都在内存中有本身的一份空间,和其它的实例之间的属性是没有关系的,值类型能够理解为一个连续的代码块,每建立一个值类型的实例,就至关于将这样的代码块复制了一份,每一份都有本身的相应的属性的值,若是实例类型是可变的,那么这个代码块中的每一个属性都是能够变的,若是一个实例类型是不可变的,那么这个代码块中的每一个属性也都是不可变的,每一个实例类型都必须可以支持属性可变和不可变,为了知足这个条件,Swift将结构体的方法分为两类,能够修改结构体结构的和不能够修改结构体结构的,修改结构体结构的方法,必须被可变的结构体实例调用;不修改结构体结构的方法,能够被可变和不可变的结构体实例调用,而大多数状况下,使用的是后者,因此有可能苹果直接将后者做为默认状况,结构体的方法不可以修改结构体实例的属性引用类型,能够理解为指针(虽然Swift中没有指针,就像Java同样,可是面向对象的语言其实是把类实例都当指针处理的),指向内存空间中的同一个位置,每建立一个引用类型的实例,就会多一个指针,指向这个内存地址。指针

    好了,下边来解决这个问题,因为值类型实例能够赋值给变量或者常量,而被赋值的常量或者变量又决定了值类型实例是否可变,进而决定了值类型的实例属性是否可变,能够理解为值类型的实例属性有两种模式,可变和不可变(由属性的类型和最后被实例赋值的常量或变量决定),如下是规则:code


    举例以下:对象

struct Point {
    var x = 0
    let y = 0
}

var a = Point()
let b = Point()

a.x = 1 // var, right.
//a.y = 2 // let, compile time error.
//b.x = 3 // let, compile time error.
//b.y = 4 // let, compile time error.

    如上所示,只有在实例属性为变量,而且最终实例赋值给一个变量的时候,才能够修改相应变量的属性,而引用类型在实例化的时候,并无进行相应的属性的复制,只是至关于添加了一个指向相应属性的指针,而属性可能又是指向其它类型的指针,因此var类型的属性仍是let类型的属性,只是肯定这个"指针"也就是对应关系是否是能够变blog

    能够理解为在结构体进行实例化以前,结构体并不知道本身是否是可变的,为了防止被误修改,默认为本身是不可变的,除非在事先声明的状况才可变,这就是"mutating"关键字的做用。内存

    下面给出代码说明值类型。get

struct Point1 {
    var x = 0, y = 0
    mutating func moveToX(x: Int, andY y:Int) { // need to be a mutating method in order to work
        self.x = x
        self.y = y
    }
}

var p1 = Point1(x: 1, y: 2) // in order to change the properties, you have to use var, since it is a value type.
p1.x = 3 // works from outside the struct.
p1.moveToX(5, andY: 5)

println("p1.x = \(p1.x), p1.y = \(p1.y)")

/***************************/
struct Point2 {
    let x = 0, y = 0
}

var p2 = Point2(x: 1, y: 2)
println("p2.x=\(p2.x), p2.y=\(p2.y)")
//p2.x = 3 // can't change p2.x, since p2.x is a constant.

    下面给出代码,说明引用类型:

class Point3 {
    var x = 0
    var y = 0
    let plet: Point4
    var pvar: Point4
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
        self.plet = Point4() // plet.x = 0, plet.y = 0
        self.pvar = Point4() // pvar.x = 0, pvar.y = 0
    }

    func moveToX(x: Int, andY y: Int) { // no need to use "mutating" keyword.
        self.x = x;
        self.y = y;
    }
}

class Point4 {
    var x = 0
    var y = 0
}

let p3 = Point3(x:1, y:2) // you can use let, even though you want to change the property, because it is a reference.
p3.x = 2
p3.moveToX(5, andY: 5) // no need to use the "mutating" keyword.
println("p3.x = \(p3.x), p3.y = \(p3.y)") // x = 5, y = 5

var p4 = p3 // p3 and p4 are the same, since they are reference type.
p4.x = 3
println("p4.x = \(p4.x), p4.y = \(p4.y)") // p4.x = 3, p4.y = 5
println("p3.x = \(p3.x), p3.y = \(p3.y)") // p3.x = 3, p3.y = 5

/**********************/
p3.plet.x = 4
println("p4.p.x = \(p3.plet.x), p4.p.y = \(p3.plet.y)") // p3.plet.x = 4, p3.plet.y = 0

let p5  = Point4()
//p3.plet = p5 // can't assign new value to p3.plet since the realtion can't change since the p property of p3 is a constant.
p3.pvar = p5 // even p3 is a constant, its propery can change.

    在上面的代码中,最重要的就是"*"号如下的代码,能够说明引用类型中的let和var的做用。

    更详细讨论,能够查看:http://stackoverflow.com/questions/24035648/swift-and-mutating-struct